@skillpie/dsh-remote 1.1.0 → 1.2.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/lib/index.js CHANGED
@@ -327,7 +327,9 @@ export default {
327
327
  }
328
328
 
329
329
  // 7. Visitor restore worker: conceals the code from the address bar and
330
- // keeps the bare origin reopening the tunnel (see concealScript).
330
+ // keeps the bare origin reopening the tunnel (see concealScript). The
331
+ // worker also probes 8-digit code paths and swaps a 404 (unknown or
332
+ // offline-revoked code) for the code-entry dialog.
331
333
  if (mode === 'gui' && (direct || relayFaceEnabled)) {
332
334
  disposers.push(webServer.register({
333
335
  kind: 'exact',
package/lib/relay-core.js CHANGED
@@ -65,6 +65,92 @@ export class RelayCore {
65
65
  this.cmdSeq = 0
66
66
  this.timers = new Set()
67
67
  this.closed = false
68
+ this.enterDisposers = []
69
+ // 未知远程码兜底:webServer 的前缀匹配是「p 或 p/」语义,任何短于 8 字符
70
+ // 的前缀都命中不了 /<code>/ 路径,所以无法用通配路由接管数字码;改为
71
+ // ①独立输码页路由(供手动访问/边缘转发目标),②由 index.js 用
72
+ // webServer.tapIndex 把守卫脚本注进服务器 SPA 文档——未知码路径命中的
73
+ // 正是 SPA fallback 渲染的 index,守卫脚本据 pathname 弹输码框。
74
+ if (this.mode === 'gui') {
75
+ try {
76
+ this.enterDisposers.push(this.webServer.register({
77
+ kind: 'prefix',
78
+ path: '/dsh-remote-enter',
79
+ handler: (req, res) => {
80
+ try { this.serveEnter(req, res) } catch (error) {
81
+ this.log('relay', `输码页请求失败(已隔离): ${error?.message ?? error}`)
82
+ try { res.writeHead(500).end() } catch { /* gone */ }
83
+ }
84
+ },
85
+ }))
86
+ } catch (error) {
87
+ this.log('relay', `输码路由注册失败(已隔离): ${error?.message ?? error}`)
88
+ }
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Standalone code-entry page: `/dsh-remote-enter?code=NNNNNNNN` renders the
94
+ * dialog pre-flagged with that code as invalid; without a code it opens the
95
+ * blank dialog. Also the redirect target for edge-level unknown-code rules.
96
+ */
97
+ serveEnter(req, res) {
98
+ let code = ''
99
+ try { code = new URL(req.url ?? '/', 'http://localhost').searchParams.get('code') ?? '' } catch { code = '' }
100
+ const send = (status, body, headers = {}) => {
101
+ if (res.headersSent) { try { res.destroy() } catch { res.end() } ; return }
102
+ res.writeHead(status, { 'Cache-Control': 'no-store', 'X-Content-Type-Options': 'nosniff', ...headers })
103
+ res.end(body)
104
+ }
105
+ const bad = /^[1-9]\d{7}$/.test(code) ? code : ''
106
+ if (code !== '' && bad === '') {
107
+ send(400, 'bad code\n', { 'Content-Type': 'text/plain; charset=utf-8' })
108
+ return
109
+ }
110
+ send(200, this.enterHtml(bad), { 'Content-Type': 'text/html; charset=utf-8' })
111
+ }
112
+
113
+ /**
114
+ * Code-entry page: shown for a well-formed but unknown remote code instead
115
+ * of the SPA's 404. Asks the visitor for a valid code and forwards to
116
+ * /<code>/, where the normal conceal+tunnel flow takes over — the entered
117
+ * code never lingers in a form, and a live code behaves exactly like
118
+ * opening its link for the first time.
119
+ */
120
+ enterHtml(badCode) {
121
+ const hint = badCode
122
+ ? `<div class="err">远程码 <code>${badCode}</code> 无效或宿主机未在线,请核对后重试。</div>`
123
+ : ''
124
+ return `<!DOCTYPE html>
125
+ <html lang="zh-CN"><head><meta charset="utf-8">
126
+ <meta name="viewport" content="width=device-width, initial-scale=1">
127
+ <meta name="robots" content="noindex">
128
+ <title>DeepSeek 远程访问</title>
129
+ <style>body{margin:0;height:100vh;display:flex;align-items:center;justify-content:center;background:#101114;color:#e8eaed;font:14px/1.6 -apple-system,"PingFang SC","Microsoft YaHei",sans-serif}
130
+ .card{width:min(360px,90vw);background:#1a1c21;border:1px solid #2a2d34;border-radius:14px;padding:28px 26px;box-sizing:border-box}
131
+ h1{margin:0 0 6px;font-size:16px;font-weight:600}.sub{margin:0 0 18px;font-size:12.5px;color:#9aa0a6}
132
+ .row{display:flex;gap:8px}
133
+ input{flex:1;min-width:0;background:#101114;border:1px solid #2a2d34;border-radius:8px;color:#e8eaed;padding:9px 12px;font:15px ui-monospace,Menlo,monospace;letter-spacing:.12em;outline:none}
134
+ input:focus{border-color:#6b84ff}
135
+ button{background:#4a5cff;border:0;border-radius:8px;color:#fff;padding:9px 16px;font:14px inherit;cursor:pointer}
136
+ button:hover{background:#5b6bff}
137
+ .err{margin-top:14px;font-size:12.5px;color:#ff8a80;word-break:break-all}code{font-family:ui-monospace,Menlo,monospace;color:#ffb4a9}
138
+ .tip{margin-top:16px;font-size:11.5px;color:#6b7078}</style></head>
139
+ <body><div class="card"><h1>DeepSeek 远程访问</h1><p class="sub">请输入 8 位远程码以连接对应的宿主机。</p>
140
+ ${hint}
141
+ <form class="row"><input name="code" inputmode="numeric" autocomplete="off" maxlength="8" placeholder="12345678" autofocus><button type="submit">进入</button></form>
142
+ <div class="tip">远程码由宿主机属主面板提供,仅在宿主机在线时可用。</div></div>
143
+ <script>(function(){
144
+ var f=document.querySelector('form');var i=document.querySelector('input');
145
+ i.addEventListener('input',function(){i.value=i.value.replace(/\\D/g,'').slice(0,8)});
146
+ f.addEventListener('submit',function(ev){
147
+ ev.preventDefault();
148
+ var c=i.value;
149
+ if(!/^[1-9]\\d{7}$/.test(c)){i.focus();return}
150
+ location.assign('/'+c+'/');
151
+ });
152
+ i.focus();
153
+ })();</script></body></html>`
68
154
  }
69
155
 
70
156
  later(fn, ms) {
@@ -951,6 +1037,8 @@ try{document.querySelectorAll('[role=menu]').forEach(patch)}catch(e){}
951
1037
 
952
1038
  detachAll() {
953
1039
  this.closed = true
1040
+ for (const dispose of this.enterDisposers) { try { dispose() } catch { /* gone */ } }
1041
+ this.enterDisposers = []
954
1042
  for (const timer of this.timers) clearTimeout(timer)
955
1043
  this.timers.clear()
956
1044
  for (const hostId of [...this.hosts.keys()]) this.removeHost(hostId, 'relay shutting down')
@@ -989,21 +1077,40 @@ export function serviceWorkerScript() {
989
1077
  return `/* dsh-remote visitor restore worker */
990
1078
  var CODE='';
991
1079
  try{CODE=new URL(self.location.href).searchParams.get('code')||''}catch(e){}
1080
+ var CODE_RE=/^\\/([1-9]\\d{7})(\\/|$)/;
992
1081
  self.addEventListener('install',function(e){self.skipWaiting()});
993
1082
  self.addEventListener('activate',function(e){e.waitUntil(self.clients.claim())});
994
1083
  self.addEventListener('fetch',function(event){
995
1084
  try{
996
1085
  var req=event.request;
1086
+ if(req.method!=='GET')return;
997
1087
  if(req.mode!=='navigate')return;
998
1088
  var url=new URL(req.url);
999
1089
  if(url.origin!==self.location.origin)return;
1000
1090
  if((url.search||'').indexOf('gui')!==-1)return;
1001
1091
  if((req.headers.get('cookie')||'').indexOf('dsh-auth-')!==-1)return;
1092
+ /* Unknown-code guard: an 8-digit path that answers 404 (no live host)
1093
+ gets the code-entry dialog instead of the bare 404 page. */
1094
+ var cm=url.pathname.match(CODE_RE);
1095
+ if(cm){
1096
+ event.respondWith((async function(){
1097
+ var probe;
1098
+ try{probe=await fetch(new Request(req.url,{method:'HEAD',cache:'no-store'}))}catch(e){return fetch(req)}
1099
+ if(probe&&probe.status===404){
1100
+ try{return await fetch('/dsh-remote-enter?code='+cm[1],{cache:'no-store'})}catch(e){return fetch(req)}
1101
+ }
1102
+ return fetch(req);
1103
+ })());
1104
+ return;
1105
+ }
1002
1106
  if(url.pathname!=='/'&&url.pathname!=='')return;
1003
1107
  if(!/^[1-9]\\d{7}$/.test(CODE))return;
1004
1108
  event.respondWith((async function(){
1005
1109
  var up;
1006
1110
  try{up=await fetch('/'+CODE+'/',{cache:'no-store'})}catch(e){return}
1111
+ if(up.status===404){
1112
+ try{return await fetch('/dsh-remote-enter?code='+CODE,{cache:'no-store'})}catch(e){return fetch(req)}
1113
+ }
1007
1114
  var ct=up.headers.get('content-type')||'';
1008
1115
  if(ct.indexOf('text/html')===-1)return up;
1009
1116
  var text=await up.text();
@@ -1023,3 +1130,4 @@ export function serveServiceWorker(req, res) {
1023
1130
  })
1024
1131
  res.end(serviceWorkerScript())
1025
1132
  }
1133
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skillpie/dsh-remote",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Remote control for DeepSeek Harness: reach any host by its 8-digit code through a relay origin such as dsh.skillpie.cn.",
5
5
  "type": "module",
6
6
  "license": "MIT",