dsh-jiuling-remote-mobile 1.0.3 → 1.0.5

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.
Files changed (2) hide show
  1. package/lib/index.js +48 -13
  2. package/package.json +1 -1
package/lib/index.js CHANGED
@@ -413,8 +413,17 @@ export function apply(ctx) {
413
413
  const controller = ctx.sessionController;
414
414
  if (controller === undefined) return;
415
415
  const originalPrompt = controller.prompt.bind(controller);
416
- controller.prompt = async (request) => {
417
- if (!remoteSessions.has(request.sessionId)) return originalPrompt(request);
416
+ // ⚠️ **签名必须带上第二个参数 `signal`**(`(request, signal)`)。
417
+ //
418
+ // 官方 `dsh-api-session-controller/lib/index.js:3096` 是
419
+ // `prompt(request, signal) { signal.throwIfAborted(); ... }` —— 它**无条件**
420
+ // 对 `signal` 调 `throwIfAborted()`。我们的包装若只声明 `(request)`,
421
+ // 转发给 `originalPrompt(request)` 时 `signal` 就是 `undefined`,
422
+ // 于是发消息必报:
423
+ // `Cannot read properties of undefined (reading 'throwIfAborted')`
424
+ // 症状是**消息根本发不出去**,而报错文案完全看不出是参数漏传。
425
+ controller.prompt = async (request, signal) => {
426
+ if (!remoteSessions.has(request.sessionId)) return originalPrompt(request, signal);
418
427
  const text = (request.content ?? [])
419
428
  .filter((part) => part.type === 'text')
420
429
  .map((part) => part.text)
@@ -549,7 +558,7 @@ export function apply(ctx) {
549
558
  }
550
559
 
551
560
  /**
552
- * 挑一个**手机本地**的 cwd 给新建的远程会话用。
561
+ * 挑一个**手机本地**的工作区给新建的远程会话用。
553
562
  *
554
563
  * ## ⚠️ 为什么不能直接用 `process.cwd()`
555
564
  *
@@ -567,24 +576,25 @@ export function apply(ctx) {
567
576
  * `files/workspace/deepseek-harness/default-workspace`(**更深一层**),
568
577
  * 两者不相等,正好踩中这个坑。
569
578
  *
570
- * 优先级:默认工作区 → 任意已有工作区 → 引擎 cwd。
571
- * 前两个都拿不到时回退 `process.cwd()`(新建的工作区会按 cwd 反向认领它,
572
- * 见 `dsh-workspace` 的 `bootstrap`/`groupsByPath`,不算死路)。
579
+ * ⚠️ **光把 cwd 设对还不够**:`sessionPaths` 索引只在该注册表启动
580
+ * (`indexHeader`)时建一次,新建的会话**不在索引里** ⇒ `sessionPath(id)`
581
+ * 返回 `undefined` ≠ `record.path`,照样被 getter 过滤掉。
582
+ * 所以创建完必须再 `attachSession()` 一次(见 `createSession`),
583
+ * 它会 `rememberSessionPath()` 把这条补进索引。
573
584
  *
574
585
  * @param ctx - 宿主 Cordis 上下文。
575
- * @returns 手机本地的绝对路径。
586
+ * @returns 工作区实体;没有可用工作区时 `undefined`。
576
587
  */
577
- function localCwdFor(ctx) {
588
+ function localWorkspaceFor(ctx) {
578
589
  try {
579
590
  const registry = ctx.get?.('workspaceRegistry');
580
591
  const all = registry?.list?.() ?? [];
581
- if (all.length === 0) return process.cwd();
592
+ if (all.length === 0) return undefined;
582
593
  const preferredId = registry?.state?.defaultWorkspaceId;
583
594
  const chosen = all.find((entity) => entity.id === preferredId) ?? all[0];
584
- const path = chosen?.path;
585
- return typeof path === 'string' && path !== '' ? path : process.cwd();
595
+ return typeof chosen?.path === 'string' && chosen.path !== '' ? chosen : undefined;
586
596
  } catch {
587
- return process.cwd();
597
+ return undefined;
588
598
  }
589
599
  }
590
600
 
@@ -623,7 +633,8 @@ async function handleBridge(ctx, config, io) {
623
633
  // ⚠️ 手机端 cwd 必须是**手机本地**的绝对路径,绝不把电脑路径传进来
624
634
  // (Android 是 posix 语义,`path.posix.isAbsolute('C:/...') === false`,
625
635
  // 且官方 createOrAdopt 会 `mkdir(cwd, {recursive:true})` 真去建目录)。
626
- const cwd = localCwdFor(ctx);
636
+ const workspace = localWorkspaceFor(ctx);
637
+ const cwd = workspace?.path ?? process.cwd();
627
638
  // ⚠️ **必须走 `sessionController.create`,不能走 `ctx.sessions.create`。**
628
639
  //
629
640
  // `ctx.sessions.create()` 只把会话塞进内存 store:持久化层既没
@@ -641,6 +652,30 @@ async function handleBridge(ctx, config, io) {
641
652
  throw new Error('sessionController.create 不可用,无法在手机本地建会话');
642
653
  }
643
654
  await ctx.sessionController.create({ sessionId, cwd });
655
+ // ⚠️ 建完必须**挂到工作区**上,否则前端看不到这个会话。
656
+ //
657
+ // 归属判据是 `dsh-workspace` 的 `sessionIds` getter:
658
+ // `record.sessionIds.filter(id => host.sessionPath(id) === record.path)`。
659
+ // 而 `sessionPaths` 索引只在注册表启动时建一次(`indexHeader`),
660
+ // 新建的会话不在索引里 ⇒ `sessionPath(id)` 是 `undefined` ≠ `record.path`
661
+ // ⇒ 被 getter 过滤掉:侧边栏没有它、composer 判成「没选工作区」而禁用。
662
+ //
663
+ // `attachSession()` 会 `rememberSessionPath()` 把这条补进索引,并写进
664
+ // `record.sessionIds` —— 正是官方「添加工作区」那条路的落点。
665
+ // 它要求 `header.cwd` 的 realpath **等于**工作区 path,所以上面的 cwd
666
+ // 必须取自同一个工作区实体,不能是 `process.cwd()`。
667
+ //
668
+ // ⚠️ 失败不致命(最坏是回到「会话在盘上、界面看不见」的旧症状),
669
+ // 所以只记日志,不让整个创建失败。
670
+ if (workspace !== undefined && typeof workspace.attachSession === 'function') {
671
+ try {
672
+ await workspace.attachSession(sessionId);
673
+ } catch (error) {
674
+ ctx.logger?.warn?.(
675
+ `[dsh-remote-mobile] 会话 ${sessionId} 挂到工作区失败(界面可能看不到它): ${String(error?.message ?? error)}`,
676
+ );
677
+ }
678
+ }
644
679
  try {
645
680
  await callRemote(config, 'createSession', { sessionId, cwd: params.path });
646
681
  } catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-jiuling-remote-mobile",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "DSH 远程目录(手机端):在选文件夹流程里多出「电脑」页签,进入电脑目录、在电脑上发起对话,并把电脑的实时消息流同步到手机本地界面(只传文本,不映射桌面)。",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",