turtlecode 0.1.0 → 0.1.1
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/assets/assets/{_basePickBy-B_7ShcHh.js → _basePickBy-BcOvpC5b.js} +1 -1
- package/assets/assets/{_baseUniq-BsW7XEuT.js → _baseUniq-D9AKNI-S.js} +1 -1
- package/assets/assets/{arc-BbW57NUb.js → arc-ClcBVsDq.js} +1 -1
- package/assets/assets/architectureDiagram-2XIMDMQ5-CRV28Qi0.js +36 -0
- package/assets/assets/{auth-callback-C2n64Qcj.js → auth-callback-D7CK9pHp.js} +1 -1
- package/assets/assets/{blockDiagram-WCTKOSBZ-C1Ni5oSK.js → blockDiagram-WCTKOSBZ-CL-sI8NU.js} +2 -2
- package/assets/assets/{c4Diagram-IC4MRINW-g8EmnUUA.js → c4Diagram-IC4MRINW-AFG5p1yt.js} +2 -2
- package/assets/assets/channel-BXqEQVGi.js +1 -0
- package/assets/assets/{chunk-4BX2VUAB-CGR6NDgJ.js → chunk-4BX2VUAB-CgNy2Qqu.js} +1 -1
- package/assets/assets/chunk-55IACEB6-Bsz0SHtO.js +1 -0
- package/assets/assets/{chunk-FMBD7UC4-CBSUaebz.js → chunk-FMBD7UC4-BRuwu34q.js} +1 -1
- package/assets/assets/chunk-JSJVCQXG-Bt0GkrjS.js +1 -0
- package/assets/assets/{chunk-KX2RTZJC-D_r6FHlr.js → chunk-KX2RTZJC-BiUvn-D7.js} +1 -1
- package/assets/assets/{chunk-NQ4KR5QH-_-NyPl-E.js → chunk-NQ4KR5QH-DH4YUQE9.js} +1 -1
- package/assets/assets/{chunk-QZHKN3VN-CMDZeFvi.js → chunk-QZHKN3VN-CrLB363X.js} +1 -1
- package/assets/assets/chunk-VVM5DH6Z-D2vG_ZPx.js +1 -0
- package/assets/assets/{chunk-WL4C6EOR-p1mj0O6S.js → chunk-WL4C6EOR-913dgDB-.js} +1 -1
- package/assets/assets/classDiagram-VBA2DB6C-gcpKg8SN.js +1 -0
- package/assets/assets/classDiagram-v2-RAHNMMFH-gcpKg8SN.js +1 -0
- package/assets/assets/clone-oMsF326O.js +1 -0
- package/assets/assets/{connection-popover-Sip4k8lc.js → connection-popover-D2MEtT6z.js} +1 -1
- package/assets/assets/{context-popover-jX8NoD9w.js → context-popover-C1ZDT4aK.js} +1 -1
- package/assets/assets/cose-bilkent-S5V4N54A-DRWnwV5H.js +1 -0
- package/assets/assets/{cssMode-xHiM5mcb.js → cssMode-DX-s2EC8.js} +2 -2
- package/assets/assets/dagre-KLK3FWXG-CN4ule0l.js +4 -0
- package/assets/assets/diagram-E7M64L7V-docJyJu4.js +24 -0
- package/assets/assets/diagram-IFDJBPK2-ChVpZldk.js +43 -0
- package/assets/assets/diagram-P4PSJMXO-Bf1-kRnd.js +24 -0
- package/assets/assets/dialog-clone-project-B1wF7jYQ.js +1 -0
- package/assets/assets/{dialog-connect-provider-BEqcXIot.js → dialog-connect-provider-Ot6Qr5dL.js} +2 -2
- package/assets/assets/dialog-create-project-BUc4DxX6.js +1 -0
- package/assets/assets/{dialog-edit-project-BfeaBlLZ.js → dialog-edit-project-BrkjzrGe.js} +1 -1
- package/assets/assets/{dialog-fork-D0oj4IQ5.js → dialog-fork-CyeKjf0e.js} +1 -1
- package/assets/assets/{dialog-manage-models-mDJUOPob.js → dialog-manage-models-CoiwPdu-.js} +1 -1
- package/assets/assets/{dialog-select-directory-BocFiQ9d.js → dialog-select-directory-K0nOY5Ky.js} +3 -3
- package/assets/assets/{dialog-select-file-CifFpv3x.js → dialog-select-file-Dx0Bny2E.js} +1 -1
- package/assets/assets/{dialog-select-mcp-Bz0DoDof.js → dialog-select-mcp-ehJzhdpz.js} +1 -1
- package/assets/assets/{dialog-select-model-unpaid-Da_CQzO4.js → dialog-select-model-unpaid-IVl97Sa_.js} +2 -2
- package/assets/assets/{dialog-select-provider-2iOfyWxy.js → dialog-select-provider-CBq6hNDx.js} +1 -1
- package/assets/assets/{dialog-select-server-DBhmJ2f8.js → dialog-select-server-DA5hATxx.js} +1 -1
- package/assets/assets/{dialog-settings-gGxknIbS.js → dialog-settings-BoOPbXZp.js} +1 -1
- package/assets/assets/{erDiagram-INFDFZHY-7L6v8ikj.js → erDiagram-INFDFZHY-kDj6gvOH.js} +2 -2
- package/assets/assets/{file-icon-CgbvTpIc.js → file-icon-DVEMs7Sx.js} +1 -1
- package/assets/assets/{flowDiagram-PKNHOUZH-aJT2T3cx.js → flowDiagram-PKNHOUZH-CsQeJUub.js} +2 -2
- package/assets/assets/{freemarker2-BgcPHQt-.js → freemarker2-B_-gEFnj.js} +2 -2
- package/assets/assets/ganttDiagram-A5KZAMGK-D1W7NciY.js +292 -0
- package/assets/assets/{ghostty-web-CyXMoQwm.js → ghostty-web-BQl0ls6m.js} +1 -1
- package/assets/assets/gitGraphDiagram-K3NZZRJ6-D4eeLV-l.js +65 -0
- package/assets/assets/{graph-CFcaDaoV.js → graph-KGuLNbW2.js} +1 -1
- package/assets/assets/{handlebars-DSpGDIPh.js → handlebars-DlnQtQmF.js} +2 -2
- package/assets/assets/{home-6Va-8zmR.js → home-BnHPimua.js} +1 -1
- package/assets/assets/{html-CNWh6Ln_.js → html-DrjCwvQg.js} +2 -2
- package/assets/assets/{htmlMode-LcU-TZaJ.js → htmlMode-C1xX0YwN.js} +3 -3
- package/assets/assets/index-DKwABhnu.js +2584 -0
- package/assets/assets/{index-DpJDxStT.css → index-DTxAHLat.css} +1 -1
- package/assets/assets/infoDiagram-LFFYTUFH-DsQ9EVx0.js +2 -0
- package/assets/assets/{ishikawaDiagram-PHBUUO56-y62OJXdI.js → ishikawaDiagram-PHBUUO56-MzetUfFK.js} +6 -6
- package/assets/assets/{javascript-CTmFPESg.js → javascript-CBQQIA5q.js} +2 -2
- package/assets/assets/{jobs-popover-DRbIcR2O.js → jobs-popover-DQmVz-e0.js} +1 -1
- package/assets/assets/{journeyDiagram-4ABVD52K-Bi9XcKs5.js → journeyDiagram-4ABVD52K-BkKgSN_9.js} +2 -2
- package/assets/assets/{jsonMode-CW1XLtuQ.js → jsonMode-B2Ye6w7y.js} +3 -3
- package/assets/assets/{kanban-definition-K7BYSVSG-DE01LQ5I.js → kanban-definition-K7BYSVSG-DIJz6AsI.js} +4 -4
- package/assets/assets/{layout-C6rurAvx.js → layout-BZKpv2Gr.js} +1 -1
- package/assets/assets/linear-COY9pyF4.js +1 -0
- package/assets/assets/{liquid-DB5uFXiY.js → liquid-CQXVmj19.js} +2 -2
- package/assets/assets/{list-BPk4NMWt.js → list-DPJi0fzr.js} +1 -1
- package/assets/assets/{mdx-Q7_EfF7H.js → mdx-QuCI1OJC.js} +2 -2
- package/assets/assets/{mermaid.core-DdXWHS-1.js → mermaid.core-bhqKFRxj.js} +10 -10
- package/assets/assets/{mindmap-definition-YRQLILUH-BajbwusW.js → mindmap-definition-YRQLILUH-CVv44i-B.js} +2 -2
- package/assets/assets/pieDiagram-SKSYHLDU-2ekrtn6z.js +30 -0
- package/assets/assets/{python-B3qjyNJj.js → python-Bx2b-xP4.js} +2 -2
- package/assets/assets/{quadrantDiagram-337W2JSQ-CZMB9UKV.js → quadrantDiagram-337W2JSQ-BoTofVsV.js} +2 -2
- package/assets/assets/{razor-CJkx1qh1.js → razor-2zgukQRj.js} +2 -2
- package/assets/assets/{requirementDiagram-Z7DCOOCP-BlyteLn_.js → requirementDiagram-Z7DCOOCP-LSodooxy.js} +2 -2
- package/assets/assets/sankeyDiagram-WA2Y5GQK-DUXSSMar.js +10 -0
- package/assets/assets/{select-CEKfrTm2.js → select-BbwOgh1Q.js} +1 -1
- package/assets/assets/{sequenceDiagram-2WXFIKYE-B2cOVhRx.js → sequenceDiagram-2WXFIKYE-C01qZdHR.js} +11 -11
- package/assets/assets/{server-row-BhbnbNJy.js → server-row-mRVGCgvL.js} +1 -1
- package/assets/assets/session-BXKBDkSI.js +34 -0
- package/assets/assets/stateDiagram-RAJIS63D-BuPj1Py4.js +1 -0
- package/assets/assets/stateDiagram-v2-FVOUBMTO-CjYACBI3.js +1 -0
- package/assets/assets/{status-popover-body-DkLpfJ2k.js → status-popover-body-9YRn8z8W.js} +2 -2
- package/assets/assets/{switch-DPq0vsit.js → switch-CHySmW8J.js} +1 -1
- package/assets/assets/{tabs-dF68ik4J.js → tabs-CKNlwTKd.js} +1 -1
- package/assets/assets/{tag--BCZ5q7_.js → tag-C4xikCZp.js} +1 -1
- package/assets/assets/{timeline-definition-YZTLITO2-C18tfizI.js → timeline-definition-YZTLITO2-CPcDqVYw.js} +2 -2
- package/assets/assets/transform-BwXaE9hv.js +1 -0
- package/assets/assets/{treemap-KZPCXAKY-C7fWdgj-.js → treemap-KZPCXAKY-Bin819KW.js} +1 -1
- package/assets/assets/{trellis-6YKi26t8.js → trellis-Csm7WAqm.js} +217 -231
- package/assets/assets/{trellis-badge-demo-BDa3ILpo.js → trellis-badge-demo-nxtlIRoh.js} +1 -1
- package/assets/assets/{trellis-op-icon-5x1lMwNH.js → trellis-op-icon-CzIuo5X7.js} +1 -1
- package/assets/assets/{tsMode-Bg19hQiu.js → tsMode-qOM514sL.js} +2 -2
- package/assets/assets/{typescript-8CtqEj8t.js → typescript-XOJMwr_m.js} +2 -2
- package/assets/assets/{vennDiagram-LZ73GAT5-DWmAgqdE.js → vennDiagram-LZ73GAT5-BDAdfrV5.js} +2 -2
- package/assets/assets/{xml-BKjz9euG.js → xml-BIwomT_0.js} +2 -2
- package/assets/assets/{xychartDiagram-JWTSCODW-Ckcyhr_q.js → xychartDiagram-JWTSCODW-Be2x0vdX.js} +2 -2
- package/assets/assets/{yaml-jJWTWPtR.js → yaml-BZMZa-4Z.js} +2 -2
- package/assets/index.html +2 -4
- package/assets/oc-theme-preload.js +0 -2
- package/bin/cli.mjs +166 -24
- package/package.json +1 -1
- package/assets/assets/architectureDiagram-2XIMDMQ5-DVePwAIo.js +0 -36
- package/assets/assets/channel-CPcPUiyc.js +0 -1
- package/assets/assets/chunk-55IACEB6-CuagpIkW.js +0 -1
- package/assets/assets/chunk-JSJVCQXG-DK5RSVJJ.js +0 -1
- package/assets/assets/chunk-VVM5DH6Z-COaOSPgd.js +0 -1
- package/assets/assets/classDiagram-VBA2DB6C-BAnp4Zaa.js +0 -1
- package/assets/assets/classDiagram-v2-RAHNMMFH-BAnp4Zaa.js +0 -1
- package/assets/assets/clone-Cf5BTVxd.js +0 -1
- package/assets/assets/cose-bilkent-S5V4N54A-C5LXU9pd.js +0 -1
- package/assets/assets/dagre-KLK3FWXG-m-9cmD-w.js +0 -4
- package/assets/assets/diagram-E7M64L7V-Cn-5LVpt.js +0 -24
- package/assets/assets/diagram-IFDJBPK2-CD8BK0Gp.js +0 -43
- package/assets/assets/diagram-P4PSJMXO-JVUlmg7w.js +0 -24
- package/assets/assets/dialog-clone-project-BgrtjUcd.js +0 -1
- package/assets/assets/dialog-create-project-TcSOut-3.js +0 -1
- package/assets/assets/ganttDiagram-A5KZAMGK-C5vwClxA.js +0 -292
- package/assets/assets/gitGraphDiagram-K3NZZRJ6-CI0SeLE3.js +0 -65
- package/assets/assets/index-bSvs-Qh3.js +0 -2544
- package/assets/assets/infoDiagram-LFFYTUFH-H6835-cD.js +0 -2
- package/assets/assets/linear-ilW0Y4uJ.js +0 -1
- package/assets/assets/pieDiagram-SKSYHLDU-DxK57epR.js +0 -30
- package/assets/assets/purify.es-A66Cw1IH.js +0 -2
- package/assets/assets/sankeyDiagram-WA2Y5GQK-DrdtRBZz.js +0 -10
- package/assets/assets/session-CdcUAq9-.js +0 -60
- package/assets/assets/stateDiagram-RAJIS63D-C4udRYnl.js +0 -1
- package/assets/assets/stateDiagram-v2-FVOUBMTO-CECUON5A.js +0 -1
package/bin/cli.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { createServer } from "node:http"
|
|
3
|
+
import { createServer, request as httpRequest } from "node:http"
|
|
4
4
|
import { readFileSync, existsSync, statSync } from "node:fs"
|
|
5
|
-
import { join, extname, dirname } from "node:path"
|
|
5
|
+
import { join, extname, dirname, resolve } from "node:path"
|
|
6
6
|
import { fileURLToPath } from "node:url"
|
|
7
|
-
import { exec } from "node:child_process"
|
|
7
|
+
import { exec, spawn } from "node:child_process"
|
|
8
8
|
|
|
9
9
|
const root = join(dirname(fileURLToPath(import.meta.url)), "..", "assets")
|
|
10
10
|
|
|
@@ -17,10 +17,14 @@ if (args.includes("--help") || args.includes("-h")) {
|
|
|
17
17
|
npx turtlecode [options]
|
|
18
18
|
|
|
19
19
|
Options:
|
|
20
|
-
--port, -p <number>
|
|
21
|
-
--
|
|
22
|
-
--
|
|
23
|
-
--
|
|
20
|
+
--port, -p <number> Port to serve on (default: 3333)
|
|
21
|
+
--backend, -b <url> OpenCode backend URL (default: http://localhost:4096)
|
|
22
|
+
--no-open Don't auto-open the browser
|
|
23
|
+
--help, -h Show this help message
|
|
24
|
+
--version, -v Show version
|
|
25
|
+
|
|
26
|
+
The backend server (opencode) is started automatically.
|
|
27
|
+
Install it with: npm i -g opencode-ai
|
|
24
28
|
`)
|
|
25
29
|
process.exit(0)
|
|
26
30
|
}
|
|
@@ -31,9 +35,72 @@ if (args.includes("--version") || args.includes("-v")) {
|
|
|
31
35
|
process.exit(0)
|
|
32
36
|
}
|
|
33
37
|
|
|
34
|
-
|
|
35
|
-
const
|
|
38
|
+
function flag(long, short) {
|
|
39
|
+
const i = Math.max(args.indexOf(long), args.indexOf(short))
|
|
40
|
+
return i !== -1 && args[i + 1] ? args[i + 1] : undefined
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const port = parseInt(flag("--port", "-p") || "3333", 10)
|
|
36
44
|
const open = !args.includes("--no-open")
|
|
45
|
+
const backend = new URL(flag("--backend", "-b") || process.env.OPENCODE_URL || "http://localhost:4096")
|
|
46
|
+
|
|
47
|
+
// URL-safe base64 encode (matches @opencode-ai/util/encode)
|
|
48
|
+
function encode(str) {
|
|
49
|
+
return Buffer.from(str).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "")
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function probe(origin) {
|
|
53
|
+
return new Promise((ok) => {
|
|
54
|
+
const req = httpRequest(origin, { method: "HEAD", timeout: 1000 }, (res) => {
|
|
55
|
+
res.resume()
|
|
56
|
+
ok(true)
|
|
57
|
+
})
|
|
58
|
+
req.on("error", () => ok(false))
|
|
59
|
+
req.on("timeout", () => {
|
|
60
|
+
req.destroy()
|
|
61
|
+
ok(false)
|
|
62
|
+
})
|
|
63
|
+
req.end()
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function boot(origin) {
|
|
68
|
+
if (await probe(origin)) return null
|
|
69
|
+
|
|
70
|
+
const url = new URL(origin)
|
|
71
|
+
if (url.hostname !== "localhost" && url.hostname !== "127.0.0.1") {
|
|
72
|
+
console.error(`\n Error: Backend not reachable at ${origin}\n`)
|
|
73
|
+
process.exit(1)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
console.log(` Starting backend on port ${url.port}...`)
|
|
77
|
+
const child = spawn("opencode", ["serve", "--port", url.port], {
|
|
78
|
+
cwd: process.cwd(),
|
|
79
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
80
|
+
env: { ...process.env },
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
let exited = false
|
|
84
|
+
child.on("exit", (code) => {
|
|
85
|
+
exited = true
|
|
86
|
+
if (code && code !== 0) console.error(`\n Backend exited with code ${code}`)
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
child.on("error", (err) => {
|
|
90
|
+
console.error(`\n Error: Could not start backend.\n ${err.message}\n\n Install: npm i -g opencode-ai\n`)
|
|
91
|
+
process.exit(1)
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
const deadline = Date.now() + 30_000
|
|
95
|
+
while (Date.now() < deadline && !exited) {
|
|
96
|
+
if (await probe(origin)) return child
|
|
97
|
+
await new Promise((r) => setTimeout(r, 300))
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (!exited) child.kill()
|
|
101
|
+
console.error("\n Error: Backend failed to start within 30s.\n")
|
|
102
|
+
process.exit(1)
|
|
103
|
+
}
|
|
37
104
|
|
|
38
105
|
const mime = {
|
|
39
106
|
".html": "text/html; charset=utf-8",
|
|
@@ -61,19 +128,63 @@ if (!existsSync(join(root, "index.html"))) {
|
|
|
61
128
|
process.exit(1)
|
|
62
129
|
}
|
|
63
130
|
|
|
131
|
+
const spawned = await boot(backend.origin)
|
|
132
|
+
|
|
133
|
+
// Determine whether a request should be proxied to the backend.
|
|
134
|
+
// Static assets are served directly; browser navigation gets the SPA
|
|
135
|
+
// index.html; everything else (API calls, SSE, etc.) is proxied.
|
|
136
|
+
function shouldProxy(req, file) {
|
|
137
|
+
// Non-GET/HEAD methods are always API requests
|
|
138
|
+
if (req.method !== "GET" && req.method !== "HEAD") return true
|
|
139
|
+
|
|
140
|
+
// If the file exists as a static asset, serve it directly
|
|
141
|
+
if (existsSync(file) && !statSync(file).isDirectory()) return false
|
|
142
|
+
|
|
143
|
+
// GET requests that accept text/html are browser navigation → SPA fallback
|
|
144
|
+
const accept = req.headers.accept || ""
|
|
145
|
+
if (accept.includes("text/html")) return false
|
|
146
|
+
|
|
147
|
+
// Everything else (JSON, SSE, etc.) → proxy
|
|
148
|
+
return true
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function proxy(req, res) {
|
|
152
|
+
const url = new URL(req.url, `http://localhost:${port}`)
|
|
153
|
+
const target = `${backend.origin}${url.pathname}${url.search}`
|
|
154
|
+
|
|
155
|
+
const headers = { ...req.headers, host: backend.host }
|
|
156
|
+
|
|
157
|
+
const proxyReq = httpRequest(target, { method: req.method, headers }, (proxyRes) => {
|
|
158
|
+
res.writeHead(proxyRes.statusCode, proxyRes.headers)
|
|
159
|
+
proxyRes.pipe(res)
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
proxyReq.on("error", () => {
|
|
163
|
+
if (res.headersSent) return res.end()
|
|
164
|
+
res.writeHead(502, { "Content-Type": "application/json" })
|
|
165
|
+
res.end(
|
|
166
|
+
JSON.stringify({
|
|
167
|
+
error: `Backend not reachable at ${backend.origin}. Start it with: opencode serve`,
|
|
168
|
+
}),
|
|
169
|
+
)
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
req.pipe(proxyReq)
|
|
173
|
+
}
|
|
174
|
+
|
|
64
175
|
const server = createServer((req, res) => {
|
|
65
176
|
const url = new URL(req.url, `http://localhost:${port}`)
|
|
66
|
-
|
|
177
|
+
const file = join(root, url.pathname === "/" ? "index.html" : url.pathname)
|
|
67
178
|
|
|
68
|
-
if (
|
|
69
|
-
file = join(root, "index.html")
|
|
70
|
-
}
|
|
179
|
+
if (shouldProxy(req, file)) return proxy(req, res)
|
|
71
180
|
|
|
72
|
-
|
|
181
|
+
// Serve static file or SPA fallback
|
|
182
|
+
const resolved = existsSync(file) && !statSync(file).isDirectory() ? file : join(root, "index.html")
|
|
183
|
+
const ext = extname(resolved)
|
|
73
184
|
const type = mime[ext] || "application/octet-stream"
|
|
74
185
|
|
|
75
186
|
try {
|
|
76
|
-
const body = readFileSync(
|
|
187
|
+
const body = readFileSync(resolved)
|
|
77
188
|
res.writeHead(200, {
|
|
78
189
|
"Content-Type": type,
|
|
79
190
|
"Content-Length": body.length,
|
|
@@ -86,26 +197,57 @@ const server = createServer((req, res) => {
|
|
|
86
197
|
}
|
|
87
198
|
})
|
|
88
199
|
|
|
200
|
+
// Handle WebSocket upgrade (used by PTY terminal connections)
|
|
201
|
+
server.on("upgrade", (req, socket, head) => {
|
|
202
|
+
const url = new URL(req.url, `http://localhost:${port}`)
|
|
203
|
+
const target = `${backend.origin}${url.pathname}${url.search}`
|
|
204
|
+
const headers = { ...req.headers, host: backend.host }
|
|
205
|
+
|
|
206
|
+
const proxyReq = httpRequest(target, { method: "GET", headers })
|
|
207
|
+
|
|
208
|
+
proxyReq.on("upgrade", (proxyRes, proxySocket, proxyHead) => {
|
|
209
|
+
socket.write(
|
|
210
|
+
`HTTP/1.1 101 Switching Protocols\r\n` +
|
|
211
|
+
Object.entries(proxyRes.headers)
|
|
212
|
+
.map(([k, v]) => `${k}: ${v}`)
|
|
213
|
+
.join("\r\n") +
|
|
214
|
+
"\r\n\r\n",
|
|
215
|
+
)
|
|
216
|
+
if (proxyHead.length) socket.write(proxyHead)
|
|
217
|
+
proxySocket.pipe(socket)
|
|
218
|
+
socket.pipe(proxySocket)
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
proxyReq.on("error", () => {
|
|
222
|
+
socket.end("HTTP/1.1 502 Bad Gateway\r\n\r\n")
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
proxyReq.end()
|
|
226
|
+
})
|
|
227
|
+
|
|
89
228
|
server.listen(port, () => {
|
|
90
|
-
const
|
|
229
|
+
const dir = encode(resolve(process.cwd()))
|
|
230
|
+
const url = `http://localhost:${port}/${dir}`
|
|
91
231
|
console.log(`
|
|
92
232
|
🐢 turtlecode v${process.env.npm_package_version || "0.1.0"}
|
|
93
233
|
|
|
94
|
-
Local:
|
|
234
|
+
Local: ${url}
|
|
235
|
+
Backend: ${backend.origin}
|
|
236
|
+
Project: ${process.cwd()}
|
|
95
237
|
Press Ctrl+C to stop
|
|
96
238
|
`)
|
|
97
239
|
|
|
98
240
|
if (open) {
|
|
99
|
-
const cmd = process.platform === "darwin"
|
|
100
|
-
? "open"
|
|
101
|
-
: process.platform === "win32"
|
|
102
|
-
? "start"
|
|
103
|
-
: "xdg-open"
|
|
241
|
+
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open"
|
|
104
242
|
exec(`${cmd} ${url}`)
|
|
105
243
|
}
|
|
106
244
|
})
|
|
107
245
|
|
|
108
|
-
|
|
246
|
+
function cleanup() {
|
|
247
|
+
if (spawned) spawned.kill()
|
|
109
248
|
console.log("\n Stopped.\n")
|
|
110
249
|
process.exit(0)
|
|
111
|
-
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
process.on("SIGINT", cleanup)
|
|
253
|
+
process.on("SIGTERM", cleanup)
|