sanjang 0.3.6 → 0.3.7
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/dashboard/app.js +34 -17
- package/dist/lib/server.js +32 -0
- package/package.json +7 -4
package/dashboard/app.js
CHANGED
|
@@ -1736,23 +1736,40 @@ function renderWorkspace(data) {
|
|
|
1736
1736
|
const previewEl = document.getElementById('ws-preview');
|
|
1737
1737
|
const previewToolbar = document.getElementById('ws-preview-toolbar');
|
|
1738
1738
|
if (previewUrl) {
|
|
1739
|
-
const
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1739
|
+
const hasExtension = !!window.__SANJANG_EXTENSION__;
|
|
1740
|
+
if (hasExtension) {
|
|
1741
|
+
previewEl.innerHTML = `
|
|
1742
|
+
<div style="display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;gap:16px;">
|
|
1743
|
+
<div style="font-size:48px">⛰</div>
|
|
1744
|
+
<div style="color:var(--text-muted);font-size:14px;text-align:center;">
|
|
1745
|
+
확장이 설치되어 있어유!<br>
|
|
1746
|
+
dev 서버를 직접 볼 수 있어유.
|
|
1747
|
+
</div>
|
|
1748
|
+
<a href="${escHtml(previewUrl)}" target="_blank" class="btn btn-primary" style="text-decoration:none">
|
|
1749
|
+
새 탭에서 열기 → ${escHtml(previewUrl)}
|
|
1750
|
+
</a>
|
|
1751
|
+
</div>`;
|
|
1752
|
+
if (previewToolbar) previewToolbar.style.display = 'none';
|
|
1753
|
+
} else {
|
|
1754
|
+
// No extension — use iframe+proxy fallback
|
|
1755
|
+
const port = new URL(previewUrl).port || '80';
|
|
1756
|
+
const proxyUrl = `/preview/${port}/`;
|
|
1757
|
+
previewEl.innerHTML = `
|
|
1758
|
+
<iframe src="${escHtml(proxyUrl)}" class="ws-preview-iframe"></iframe>
|
|
1759
|
+
<div class="ws-preview-fallback" style="display:none">
|
|
1760
|
+
<a href="${escHtml(previewUrl)}" target="_blank" class="btn btn-primary">
|
|
1761
|
+
새 탭에서 열기 → ${escHtml(previewUrl)}
|
|
1762
|
+
</a>
|
|
1763
|
+
</div>`;
|
|
1764
|
+
const iframe = previewEl.querySelector('iframe');
|
|
1765
|
+
iframe.addEventListener('error', () => {
|
|
1766
|
+
iframe.style.display = 'none';
|
|
1767
|
+
previewEl.querySelector('.ws-preview-fallback').style.display = 'flex';
|
|
1768
|
+
});
|
|
1769
|
+
if (previewToolbar) previewToolbar.style.display = '';
|
|
1770
|
+
updateUrlBar('/');
|
|
1771
|
+
if (currentViewport !== 'desktop') setViewport(currentViewport);
|
|
1772
|
+
}
|
|
1756
1773
|
} else {
|
|
1757
1774
|
if (previewToolbar) previewToolbar.style.display = 'none';
|
|
1758
1775
|
previewEl.innerHTML = `
|
package/dist/lib/server.js
CHANGED
|
@@ -540,6 +540,38 @@ export async function createApp(projectRoot, options = {}) {
|
|
|
540
540
|
// -------------------------------------------------------------------------
|
|
541
541
|
// REST API
|
|
542
542
|
// -------------------------------------------------------------------------
|
|
543
|
+
// CORS for extension — localhost cross-port requests
|
|
544
|
+
app.use("/api/", (req, res, next) => {
|
|
545
|
+
const origin = req.headers.origin || "";
|
|
546
|
+
if (origin.startsWith("http://localhost:") || origin.startsWith("http://127.0.0.1:") ||
|
|
547
|
+
origin.startsWith("https://localhost:") || origin.startsWith("https://127.0.0.1:")) {
|
|
548
|
+
res.setHeader("Access-Control-Allow-Origin", origin);
|
|
549
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS");
|
|
550
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
|
551
|
+
}
|
|
552
|
+
if (req.method === "OPTIONS")
|
|
553
|
+
return res.sendStatus(204);
|
|
554
|
+
next();
|
|
555
|
+
});
|
|
556
|
+
// Extension API — 확장이 현재 포트가 산장 캠프인지 확인
|
|
557
|
+
app.get("/api/camps/by-port/:port", (req, res) => {
|
|
558
|
+
const targetPort = parseInt(req.params.port, 10);
|
|
559
|
+
if (!Number.isFinite(targetPort))
|
|
560
|
+
return res.status(400).json({ error: "invalid port" });
|
|
561
|
+
const camps = getAll();
|
|
562
|
+
const camp = camps.find((c) => c.fePort === targetPort && c.status === "running");
|
|
563
|
+
if (!camp)
|
|
564
|
+
return res.status(404).json({ error: "not found" });
|
|
565
|
+
res.json({
|
|
566
|
+
name: camp.name,
|
|
567
|
+
branch: camp.branch,
|
|
568
|
+
status: camp.status,
|
|
569
|
+
fePort: camp.fePort,
|
|
570
|
+
});
|
|
571
|
+
});
|
|
572
|
+
app.get("/api/extension/version", (_req, res) => {
|
|
573
|
+
res.json({ serverVersion: "0.3.7", minExtVersion: "0.1.0" });
|
|
574
|
+
});
|
|
543
575
|
// Project info — used by dashboard header
|
|
544
576
|
const projectName = projectRoot.split("/").pop() ?? "project";
|
|
545
577
|
app.get("/api/project", (_req, res) => res.json({ name: projectName }));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sanjang",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.7",
|
|
4
4
|
"description": "AI dev environment for vibe coders — camp isolation, self-healing, smart PR",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -9,10 +9,12 @@
|
|
|
9
9
|
"scripts": {
|
|
10
10
|
"start": "node bin/sanjang.js",
|
|
11
11
|
"build": "tsc -p tsconfig.build.json",
|
|
12
|
-
"prepublishOnly": "npm run build && echo '#!/usr/bin/env node' | cat - dist/bin/sanjang.js > dist/bin/_tmp && mv dist/bin/_tmp dist/bin/sanjang.js",
|
|
12
|
+
"prepublishOnly": "npm run build && npm run ext:build && echo '#!/usr/bin/env node' | cat - dist/bin/sanjang.js > dist/bin/_tmp && mv dist/bin/_tmp dist/bin/sanjang.js",
|
|
13
13
|
"test": "node --experimental-transform-types --test test/**/*.test.ts bin/__tests__/*.test.ts",
|
|
14
14
|
"typecheck": "tsc --noEmit",
|
|
15
|
-
"lint": "npx @biomejs/biome check lib/ test/ bin/"
|
|
15
|
+
"lint": "npx @biomejs/biome check lib/ test/ bin/",
|
|
16
|
+
"ext:dev": "cd extension && npx wxt",
|
|
17
|
+
"ext:build": "cd extension && npx wxt build"
|
|
16
18
|
},
|
|
17
19
|
"dependencies": {
|
|
18
20
|
"express": "^4.19.2",
|
|
@@ -36,7 +38,8 @@
|
|
|
36
38
|
"files": [
|
|
37
39
|
"dist/",
|
|
38
40
|
"dashboard/",
|
|
39
|
-
"templates/"
|
|
41
|
+
"templates/",
|
|
42
|
+
"extension/.output/chrome-mv3/"
|
|
40
43
|
],
|
|
41
44
|
"devDependencies": {
|
|
42
45
|
"@biomejs/biome": "^2.4.10",
|