dsh-jiuling-remote-mobile 1.0.9 → 1.0.10
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/client.js +4 -5
- package/lib/index.js +32 -12
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -313,7 +313,7 @@ window.__ModuleLoader__.load({
|
|
|
313
313
|
* 配对状态(与目录浏览无关,所以单独一个 state)。
|
|
314
314
|
*
|
|
315
315
|
* `endpoint === null` 表示还没查过;`paired === false` 表示
|
|
316
|
-
*
|
|
316
|
+
* 手机还没有任何凭据(令牌为空),需要用户去电脑上取一个配对码。
|
|
317
317
|
*/
|
|
318
318
|
var pairPair = react.useState({ endpoint: null, paired: false, code: '', busy: false, message: null });
|
|
319
319
|
var pairState = pairPair[0];
|
|
@@ -372,10 +372,9 @@ window.__ModuleLoader__.load({
|
|
|
372
372
|
react.useEffect(
|
|
373
373
|
function () {
|
|
374
374
|
if (!open) return undefined;
|
|
375
|
-
// ⚠️
|
|
376
|
-
//
|
|
377
|
-
//
|
|
378
|
-
// 完全不知道要先去配对。这里直接给出可操作的提示。
|
|
375
|
+
// ⚠️ **没配对就别去问电脑要目录**。未配对时手机没有任何凭据,
|
|
376
|
+
// 打过去必然是 401/502 —— 用户看到的是转圈 10 秒后一句
|
|
377
|
+
// 「远程请求失败」,完全不知道要先去配对。这里直接给出可操作的提示。
|
|
379
378
|
if (tab === 'desktop' && pairState.paired !== true) {
|
|
380
379
|
patch({ loading: false, listing: null, error: null });
|
|
381
380
|
return undefined;
|
package/lib/index.js
CHANGED
|
@@ -105,24 +105,41 @@ function saveConfig(endpoint, token) {
|
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
/**
|
|
108
|
-
*
|
|
108
|
+
* 令牌为空 —— 表示**尚未配对**。
|
|
109
|
+
*
|
|
110
|
+
* ⚠️ 早期这里硬编码了一个「内置访问令牌」,让两端装完即连。那是真实漏洞:
|
|
111
|
+
* 常量随 npm 包公开,任何未配对的电脑都跑在**同一个公开令牌**上,谁都能连上去
|
|
112
|
+
* 读他的电脑(实测用包内公开令牌打公网隧道 → HTTP 200,读到 177 个目录条目)。
|
|
113
|
+
* 现在**包里不含任何凭据**:令牌只能由配对流程从服务器 claim 得到(见
|
|
114
|
+
* `claimPairing`),未配对时就是空字符串,`callRemote` / `follow` 会直接报
|
|
115
|
+
* 「尚未配对」,不会静默用一个假令牌去连。
|
|
116
|
+
*/
|
|
117
|
+
const EMPTY_TOKEN = '';
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* **已作废**的旧公开令牌 —— 只用于识别,**不是凭据**。
|
|
109
121
|
*
|
|
110
|
-
*
|
|
111
|
-
* `
|
|
122
|
+
* 1.0.9 及更早的版本把它当默认令牌写进 `mobile.json`。老用户升级后文件里还留着
|
|
123
|
+
* 这个值,若不识别就会被 `pairStatus` 判成「已配对」,而实际上电脑端早已改用随机
|
|
124
|
+
* 令牌 —— 症状是「显示已配对,一发消息就 401」。所以见到它就当成未配对。
|
|
112
125
|
*/
|
|
113
|
-
const
|
|
126
|
+
const LEGACY_PUBLIC_TOKEN = 'f6721b0cf82414709df172056193c78a6c374e9a920e51145dd7883923cf16e7';
|
|
114
127
|
|
|
115
128
|
/**
|
|
116
129
|
* 读配置。首次运行写一份模板,用户直接改文件即可。
|
|
117
130
|
*
|
|
118
|
-
*
|
|
131
|
+
* ⚠️ 这里**没有**任何内置令牌兜底(早期有,是漏洞)。`token` 为空即「未配对」,
|
|
132
|
+
* 调用方据此直接拒绝,而不是拿一个包里公开的常量去连。
|
|
119
133
|
*
|
|
120
134
|
* @returns `{ endpoint, token }`。
|
|
121
135
|
*/
|
|
122
136
|
function loadConfig() {
|
|
123
137
|
try {
|
|
124
138
|
const parsed = JSON.parse(readFileSync(CONFIG_PATH, 'utf8'));
|
|
125
|
-
|
|
139
|
+
// ⚠️ 旧版写进文件的公开令牌要当成**未配对**,否则界面显示已配对、
|
|
140
|
+
// 实际一调用就 401(电脑端已换随机令牌)。
|
|
141
|
+
const raw = typeof parsed.token === 'string' ? parsed.token : EMPTY_TOKEN;
|
|
142
|
+
const token = raw === LEGACY_PUBLIC_TOKEN ? EMPTY_TOKEN : raw;
|
|
126
143
|
return {
|
|
127
144
|
endpoint:
|
|
128
145
|
typeof parsed.endpoint === 'string' && parsed.endpoint !== ''
|
|
@@ -135,13 +152,13 @@ function loadConfig() {
|
|
|
135
152
|
mkdirSync(STATE_DIR, { recursive: true });
|
|
136
153
|
writeFileSync(
|
|
137
154
|
CONFIG_PATH,
|
|
138
|
-
JSON.stringify({ endpoint: DEFAULT_ENDPOINT, token:
|
|
155
|
+
JSON.stringify({ endpoint: DEFAULT_ENDPOINT, token: EMPTY_TOKEN }, null, 2),
|
|
139
156
|
{ mode: 0o600 },
|
|
140
157
|
);
|
|
141
158
|
} catch {
|
|
142
159
|
/* 只读环境:用默认值继续 */
|
|
143
160
|
}
|
|
144
|
-
return { endpoint: DEFAULT_ENDPOINT, token:
|
|
161
|
+
return { endpoint: DEFAULT_ENDPOINT, token: EMPTY_TOKEN };
|
|
145
162
|
}
|
|
146
163
|
}
|
|
147
164
|
|
|
@@ -218,7 +235,8 @@ async function remoteFetch(url, init) {
|
|
|
218
235
|
*/
|
|
219
236
|
async function callRemote(config, method, params = {}) {
|
|
220
237
|
if (config.token === '') {
|
|
221
|
-
|
|
238
|
+
// 未配对 = 没有令牌。**不要**提示用户去手改文件:面向大众的主路径是配对。
|
|
239
|
+
throw new Error('还没配对:请在电脑的「设置 → 手机远程」里生成配对码,再到「添加工作区 → 电脑」填进去');
|
|
222
240
|
}
|
|
223
241
|
const res = await remoteFetch(`${config.endpoint}/api/dsh-remote/rpc`, {
|
|
224
242
|
method: 'POST',
|
|
@@ -352,7 +370,7 @@ export function apply(ctx) {
|
|
|
352
370
|
const config = loadConfig();
|
|
353
371
|
ctx.logger?.info?.(`[dsh-remote-mobile] 远端: ${config.endpoint}`);
|
|
354
372
|
if (config.token === '') {
|
|
355
|
-
ctx.logger?.
|
|
373
|
+
ctx.logger?.info?.('[dsh-remote-mobile] 尚未配对:请到「添加工作区 → 电脑」填配对码(配对后自动连接)');
|
|
356
374
|
}
|
|
357
375
|
|
|
358
376
|
/** 远程会话 id 集合:这些会话的 prompt 转发电脑,不在手机本地跑模型。 */
|
|
@@ -838,7 +856,7 @@ async function handleBridge(ctx, config, io) {
|
|
|
838
856
|
throw new Error('请先在电脑页签里选一个目录');
|
|
839
857
|
}
|
|
840
858
|
if (config.token === '') {
|
|
841
|
-
throw new Error(
|
|
859
|
+
throw new Error('还没配对:请先在「电脑」页签里填配对码');
|
|
842
860
|
}
|
|
843
861
|
const sessionId = `session-${randomUUID()}`;
|
|
844
862
|
// ⚠️ 手机端 cwd 必须是**手机本地**的绝对路径,绝不把电脑路径传进来
|
|
@@ -928,7 +946,9 @@ async function handleBridge(ctx, config, io) {
|
|
|
928
946
|
|
|
929
947
|
/** 当前连的是哪台电脑(前端用来显示状态)。 */
|
|
930
948
|
case 'pairStatus':
|
|
931
|
-
|
|
949
|
+
// 「已配对」的判据是**有令牌**。早期拿「令牌不等于内置常量」当判据,
|
|
950
|
+
// 那个常量已删(它是公开凭据,见 EMPTY_TOKEN 的注释)。
|
|
951
|
+
return { endpoint: config.endpoint, paired: config.token !== EMPTY_TOKEN };
|
|
932
952
|
|
|
933
953
|
default:
|
|
934
954
|
throw new Error(`unknown method "${String(io.body?.method)}"`);
|