chore(rebar): Pier_T_rebar 작업 폴더 초기 구성 및 외부 의존 파일 통합

- Code\rebar_pipeline, review_pipeline, pier-program을 정션에서 실제 폴더로 전환
- Code\rhino_mcp 및 CLAUDE_rhino배경.md 추가, .mcp.json은 PYTHONPATH로 새 위치 사용
- pier-program 내부 .git 제거(이력은 backup\pier-program_20261028.bundle에 보관)
- worktree 가이드 반영(CLAUDE.local.md, HANDOFF.md)
- D:\Rhino-MCP 경로 참조를 새 위치로 치환

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ahnyongjooandClaude Sonnet 5.5 committed 2026-10-28 11:20:18 +09:00
commit 964a841de1
3978 files changed
+58013356

No files matched your search

@@ -0,0 +1,84 @@
"""Deployment-only HTTP prefix adapter; modeling code remains unchanged."""
import http.client
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
import re
import subprocess
import sys
from urllib.parse import urlsplit
PREFIX = '/box-sample'
UPSTREAM_PORT = 8097
HOP = {'connection', 'transfer-encoding', 'keep-alive', 'upgrade',
'proxy-authenticate', 'proxy-authorization', 'te', 'trailer'}
ROOT_URL = re.compile(rb'''(["'`])/(api/|src/|dxf_rasterizer\.html|model-report\.html)''')
def rewrite(payload):
return ROOT_URL.sub(lambda m: m[1] + PREFIX.encode() + b'/' + m[2], payload)
class Gateway(BaseHTTPRequestHandler):
def forward(self):
parsed = urlsplit(self.path)
if parsed.path == PREFIX:
self.send_response(308)
self.send_header('Location', PREFIX + '/' + ('?' + parsed.query if parsed.query else ''))
self.send_header('Content-Length', '0')
self.end_headers()
return
if not parsed.path.startswith(PREFIX + '/'):
self.send_error(404)
return
if self.headers.get('Transfer-Encoding'):
self.send_error(411, 'Content-Length required')
return
target = self.path[len(PREFIX):]
connection = http.client.HTTPConnection('127.0.0.1', UPSTREAM_PORT, timeout=600)
try:
size = int(self.headers.get('Content-Length', '0'))
if size < 0 or size > 512 * 1024 * 1024:
self.send_error(413)
return
headers = {k: v for k, v in self.headers.items()
if k.lower() not in HOP | {'accept-encoding', 'host'}}
headers['Host'] = '127.0.0.1:' + str(UPSTREAM_PORT)
connection.request(self.command, target, self.rfile.read(size) if size else None, headers)
response = connection.getresponse()
content_type = response.getheader('Content-Type', '').lower()
text = any(t in content_type for t in ('json', 'javascript', 'text/html', 'text/css'))
body = rewrite(response.read()) if text and self.command != 'HEAD' else None
self.send_response(response.status)
for key, value in response.getheaders():
if key.lower() in HOP | {'content-length', 'etag', 'content-md5'}:
continue
if key.lower() == 'location' and value.startswith('/') and not value.startswith('//'):
value = PREFIX + value
self.send_header(key, value)
self.send_header('X-DMS-Deployment', 'box-sample')
self.send_header('Cache-Control', 'no-store')
if body is not None:
self.send_header('Content-Length', str(len(body)))
elif response.getheader('Content-Length') is not None:
self.send_header('Content-Length', response.getheader('Content-Length'))
self.end_headers()
if self.command != 'HEAD':
if body is not None:
self.wfile.write(body)
else:
while chunk := response.read(65536):
self.wfile.write(chunk)
except (ValueError, OSError, http.client.HTTPException):
self.send_error(502, 'Application not ready')
finally:
connection.close()
do_GET = do_POST = do_HEAD = do_PUT = do_DELETE = forward
if __name__ == '__main__':
child = subprocess.Popen([sys.executable, 'src/backend/serve.py', '--host', '127.0.0.1', '--port', str(UPSTREAM_PORT)])
try:
ThreadingHTTPServer(('0.0.0.0', 8080), Gateway).serve_forever()
finally:
child.terminate()
child.wait(timeout=15)