dsh-connect-minimaxcode 0.6.2 → 0.6.4

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/README.en.md CHANGED
@@ -384,6 +384,19 @@ The OAuth token is valid for about an hour and is refreshed automatically by the
384
384
 
385
385
  ## Changelog
386
386
 
387
+ ### 0.6.4 (2026-11-11)
388
+
389
+ - **Fixed "the self-check report never appeared"**: the previous version put the self-check in the client half (the card reports what the host offered), but **the server half was never landed** — the status document had no field to carry the report and no route to receive it, so the card reported into nowhere. It is complete now: the report is received by the plugin's own route and appears in the status document as `capability`.
390
+ - The episode also exposed a process problem: a few anchors in the patch script missed, and its output was covered by another script in the same command, so a partial application was read as a complete one. **Every patch is now checked individually**, and an end-to-end check was added: the report counts as working only once it has travelled from the card through the route and back into the status document.
391
+ - The route-count cases were brought up to date (the host now mounts four routes: status, action, capability, generation).
392
+
393
+ ### 0.6.3 (2026-11-11)
394
+
395
+ - **"Could not send it to the conversation" now names the missing piece**: delivering a picture needs several pieces of the host — the session identifier, the sessions service, the session binding, a way to send or to hold a draft, and the input box's attachment channel — and they arrive in different ways. There used to be one blanket failure message; each absence now has its own: the host named no session / offers no sessions service / this session is not reachable / it cannot send / it cannot hold drafts / the input box takes no attachments.
396
+ - **The plugin checks itself and reports**: the card tells the plugin's own route what the host actually offered when it mounts, so the status document carries that report — working on a failure no longer depends only on the sentence on the screen.
397
+ - Also fixed a real defect the tests caught at once: the report referred to the session identifier before it was defined, and the host fires effects early, so the script broke.
398
+ - `tests/generate-dock.spec.ts` grows to 8 cases, covering the report's content and the wording when no session is named.
399
+
387
400
  ### 0.6.2 (2026-11-11)
388
401
 
389
402
  - **Fixed "could not send it to the conversation"**: the previous version looked for the session among the slot's props, but the host hands the session to the slot's **injection callback** (the slot is session-scoped), so the identifier was always missing and the picture had nowhere to go. The session is now taken the way the host gives it.
package/README.md CHANGED
@@ -395,6 +395,19 @@ OAuth 令牌有效期约 1 小时,由客户端自动续期。插件**只在自
395
395
 
396
396
  ## 更新记录
397
397
 
398
+ ### 0.6.4(2026-11-11)
399
+
400
+ - **修复「自检报告从未出现」**:上一版把自检能力加在了客户端(卡片上报宿主提供了什么),但**服务端那一半没有落地** —— 状态文档里没有承载报告的字段,也没有接收报告的路由,于是卡片上报到哪里都无人接收。现已补齐:报告由插件自己的路由接收,并出现在状态文档的 `capability` 里。
401
+ - 这次同时暴露了一个流程问题:补丁脚本里有几处锚点未命中,而脚本的输出被同一条命令里的另一个脚本盖住了,于是「部分应用」被当成了「全部应用」。**现在每一处补丁的成败都单独核对**,并且加了端到端验证:报告从卡片出发、经路由回到状态文档,全程贯通才算通过。
402
+ - 顺带补上路由数量的用例(宿主侧现在有四条路由:状态、动作、自检、生成)。
403
+
404
+ ### 0.6.3(2026-11-11)
405
+
406
+ - **「无法发送到对话」现在会说明缺哪一步**:把图片送进对话需要宿主的好几块能力(会话标识、会话服务、会话绑定、发送或草稿通道、输入框的附件通道),它们分别以不同方式交到插件手上。原先只有一句笼统的失败提示,现在每种缺失都有各自的话:宿主没给会话标识 / 没有会话服务 / 当前会话不可达 / 会话不支持发送 / 不支持草稿 / 输入框不接受附件。
407
+ - **插件会自检并上报**:卡片在挂载时把宿主实际提供了什么报告给插件自己的路由,因此状态文档里能读到这份自检结果 —— 排查时不必只靠屏幕上的那句话。
408
+ - 顺带修掉一处真实缺陷:自检报告引用了后面才定义的会话标识,宿主在效果阶段先跑,导致脚本崩溃(测试当场抓到)。
409
+ - `tests/generate-dock.spec.ts` 增至 8 个用例:覆盖自检报告的内容、以及缺会话标识时的话术。
410
+
398
411
  ### 0.6.2(2026-11-11)
399
412
 
400
413
  - **修复「无法发送到对话」**:上一版从插槽的 props 里找会话标识,但宿主是把会话交给**插槽的注入回调**的(该插槽是会话作用域的),于是标识始终为空,图片自然送不出去。现在按宿主的方式取会话标识。
package/lib/client.js CHANGED
@@ -20,6 +20,7 @@
20
20
  const STATUS_PATH = '/plugins/dsh-connect-minimaxcode/status';
21
21
  const ACTION_PATH = '/plugins/dsh-connect-minimaxcode/action';
22
22
  const GENERATE_PATH = '/plugins/dsh-connect-minimaxcode/generate';
23
+ const CAPABILITY_PATH = '/plugins/dsh-connect-minimaxcode/capability';
23
24
  const POLL_INTERVAL_MS = 20000;
24
25
  /**
25
26
  * The provider id the host side registers with `llm.registerAdapter`.
@@ -99,6 +100,12 @@
99
100
  generateSent: '图片已发送到对话',
100
101
  generateDrafted: '图片已放入输入框',
101
102
  generateSendFailed: '无法发送到对话,请到插件页生成后下载。',
103
+ generateNoSession: '宿主没有给出会话标识,无法定位对话。',
104
+ generateNoSessions: '宿主没有提供会话服务,无法定位对话。',
105
+ generateNoBinding: '当前会话不可达(宿主没有给出它的绑定)。',
106
+ generateNoPrompt: '当前会话不支持发送消息。',
107
+ generateNoDraft: '当前会话不支持草稿附件。',
108
+ generateNoInput: '输入框不接受附件。',
102
109
  };
103
110
  const en = {
104
111
  title: 'MiniMax Code credits',
@@ -171,6 +178,12 @@
171
178
  generateSent: 'The picture is on its way to the conversation',
172
179
  generateDrafted: 'The picture is in the input box',
173
180
  generateSendFailed: 'Could not send it to the conversation; generate on the plugin page and download it instead.',
181
+ generateNoSession: 'The host named no session, so the conversation cannot be located.',
182
+ generateNoSessions: 'The host offers no sessions service, so the conversation cannot be located.',
183
+ generateNoBinding: 'This session is not reachable (the host gave no binding for it).',
184
+ generateNoPrompt: 'This session cannot send messages.',
185
+ generateNoDraft: 'This session cannot hold draft attachments.',
186
+ generateNoInput: 'The input box does not take attachments.',
174
187
  };
175
188
  const loader = globalThis.__ModuleLoader__;
176
189
  if (loader === undefined)
@@ -454,17 +467,51 @@
454
467
  return template.replace(/\\{(\\w+)\\}/g, (_match, name) => String(params?.[name] ?? ''));
455
468
  });
456
469
  const [open, setOpen] = React.useState(false);
457
- const [prompt, setPrompt] = React.useState('');
458
- const [busy, setBusy] = React.useState(false);
459
- const [note, setNote] = React.useState('');
460
470
  /**
461
471
  * The session the picture belongs to.
462
472
  *
463
473
  * The host hands the session id to the slot through its injection,
464
474
  * because the slot is session-scoped; it is also carried on the
465
- * standard props, so either may deliver it.
475
+ * standard props, so either may deliver it. It is read here, before
476
+ * anything else in the dock uses it, because an effect can fire
477
+ * before the rest of the body has run.
466
478
  */
467
479
  const sessionId = () => props.sessionId;
480
+ /**
481
+ * What the host offers this dock, described once.
482
+ *
483
+ * Delivering a picture needs several pieces of the host and they
484
+ * arrive in different ways, so a failure otherwise reads as one
485
+ * undifferentiated message. The report is sent to the plugin's own
486
+ * route, so it can be read from the status document — by whoever is
487
+ * looking at the problem, not only by the person who hit it.
488
+ */
489
+ const reportCapability = () => {
490
+ const id = sessionId();
491
+ const services = clientContext?.sessions;
492
+ const binding = id === undefined ? undefined : services?.binding?.(id);
493
+ const scoped = id === undefined ? undefined : services?.scope?.(id);
494
+ const conversation = scoped?.get?.('conversation');
495
+ const findings = {
496
+ sessionId: id === undefined ? 'missing' : 'present',
497
+ sessionsService: services === undefined ? 'missing' : 'present',
498
+ binding: binding === undefined ? 'missing' : 'present',
499
+ sessionPrompt: typeof binding?.session?.prompt === 'function' ? 'present' : 'missing',
500
+ scope: scoped === undefined ? 'missing' : 'present',
501
+ conversationService: conversation === undefined ? 'missing' : 'present',
502
+ createDrafts: typeof conversation?.createDrafts === 'function' ? 'present' : 'missing',
503
+ inputActions: typeof props.inputActions?.addAttachments === 'function' ? 'present' : 'missing',
504
+ };
505
+ void fetch(`${CAPABILITY_PATH}?report=${encodeURIComponent(JSON.stringify(findings))}`).catch(() => {
506
+ // The report is a courtesy; failing to send it changes nothing.
507
+ });
508
+ };
509
+ React.useEffect(() => {
510
+ reportCapability();
511
+ }, []);
512
+ const [prompt, setPrompt] = React.useState('');
513
+ const [busy, setBusy] = React.useState(false);
514
+ const [note, setNote] = React.useState('');
468
515
  /**
469
516
  * The session's own send path.
470
517
  *
@@ -542,6 +589,14 @@
542
589
  // Sending the picture as a turn is what the dock promises; the
543
590
  // draft channel is the fallback for a host that only offers that.
544
591
  const face = sessionFace();
592
+ if (face === undefined) {
593
+ const id = sessionId();
594
+ setNote(id === undefined
595
+ ? t('generateNoSession')
596
+ : clientContext?.sessions === undefined
597
+ ? t('generateNoSessions')
598
+ : t('generateNoBinding'));
599
+ }
545
600
  if (face !== undefined) {
546
601
  await face.prompt([part, { type: 'text', text }], 'queue', undefined, `minimax-h3-${Date.now()}`);
547
602
  setPrompt('');
@@ -553,7 +608,19 @@
553
608
  }
554
609
  const drafts = draftFace();
555
610
  const addAttachments = props.inputActions?.addAttachments;
556
- if (drafts !== undefined && typeof addAttachments === 'function') {
611
+ if (drafts === undefined) {
612
+ setNote(sessionId() === undefined
613
+ ? t('generateNoSession')
614
+ : clientContext?.sessions === undefined
615
+ ? t('generateNoSessions')
616
+ : t('generateNoDraft'));
617
+ return;
618
+ }
619
+ if (typeof addAttachments !== 'function') {
620
+ setNote(t('generateNoInput'));
621
+ return;
622
+ }
623
+ {
557
624
  const file = new File([base64Bytes(body.dataUrl)], 'minimax-h3-image.jpg', { type: body.mediaType ?? 'image/jpeg' });
558
625
  const created = drafts.createDrafts(sessionId(), [file], true);
559
626
  addAttachments(created.map(draft => draft.id));
package/lib/index.d.ts CHANGED
@@ -1087,6 +1087,13 @@ interface StatusDocument {
1087
1087
  usageProfile?: UsageProfileStatus;
1088
1088
  /** The choices the generation endpoints offer. */
1089
1089
  generation?: GenerationOptions;
1090
+ /** What the host offered the card the last time it looked. */
1091
+ capability?: {
1092
+ /** When the card reported it. */
1093
+ at: number;
1094
+ /** One entry per piece the card looked for. */
1095
+ findings: Record<string, string>;
1096
+ };
1090
1097
  }
1091
1098
  /** Inputs the document is derived from. */
1092
1099
  interface StatusDeps {
@@ -1119,6 +1126,13 @@ interface StatusDeps {
1119
1126
  usageProfile?: UsageProfileStatus;
1120
1127
  /** The choices the generation endpoints offer. */
1121
1128
  generation?: GenerationOptions;
1129
+ /** What the host offered the card the last time it looked. */
1130
+ capability?: {
1131
+ /** When the card reported it. */
1132
+ at: number;
1133
+ /** One entry per piece the card looked for. */
1134
+ findings: Record<string, string>;
1135
+ };
1122
1136
  }
1123
1137
  /**
1124
1138
  * Build the card's status document.
package/lib/index.js CHANGED
@@ -746,6 +746,16 @@ const MINIMAX_ACTION_PATH = "/plugins/dsh-connect-minimaxcode/action";
746
746
  * takes a minute and a half and is polled.
747
747
  */
748
748
  const MINIMAX_GENERATE_PATH = "/plugins/dsh-connect-minimaxcode/generate";
749
+ /**
750
+ * Route the card reports what the host offers it on.
751
+ *
752
+ * Delivering a generated picture into a conversation needs several pieces of
753
+ * the host — the session, its face, the input box's attachment channel — and
754
+ * they are handed over in different ways. Reporting what is actually there
755
+ * turns "it did not work" into "this piece is missing", which is the only
756
+ * useful answer when something like that goes wrong.
757
+ */
758
+ const MINIMAX_CAPABILITY_PATH = "/plugins/dsh-connect-minimaxcode/capability";
749
759
  const HINTS = {
750
760
  "app-missing": "未找到 MiniMax Code 的登录信息。请先安装并登录 MiniMax Code 桌面 App,然后重启 DSH。",
751
761
  "not-signed-in": "MiniMax Code 尚未登录。请在 MiniMax Code 桌面 App 中登录后重启 DSH。",
@@ -776,6 +786,7 @@ function statusDocument(deps) {
776
786
  ...deps.signin === void 0 ? {} : { signin: deps.signin },
777
787
  ...deps.login === void 0 ? {} : { login: deps.login },
778
788
  ...deps.usageProfile === void 0 ? {} : { usageProfile: deps.usageProfile },
789
+ ...deps.capability === void 0 ? {} : { capability: deps.capability },
779
790
  ...deps.generation === void 0 ? {} : { generation: deps.generation }
780
791
  };
781
792
  if (auth.state === "expired") return {
@@ -2646,6 +2657,8 @@ function apply(ctx) {
2646
2657
  let loginPending = false;
2647
2658
  /** Whether the server refused the plugin's own credential. */
2648
2659
  let revokedByServer = false;
2660
+ /** What the card last reported about what the host offered it. */
2661
+ let capability;
2649
2662
  /**
2650
2663
  * Give up on the plugin's own credential after the server refused it.
2651
2664
  *
@@ -3092,6 +3105,7 @@ function apply(ctx) {
3092
3105
  signin: buildSigninStatus(),
3093
3106
  login: buildLoginStatus(),
3094
3107
  usageProfile: buildUsageProfileStatus(),
3108
+ capability,
3095
3109
  generation: {
3096
3110
  imageModels: [...IMAGE_MODELS],
3097
3111
  imageAspectRatios: [...IMAGE_ASPECT_RATIOS],
@@ -3407,6 +3421,36 @@ function apply(ctx) {
3407
3421
  }
3408
3422
  }
3409
3423
  }));
3424
+ webCtx.effect(() => webCtx.webServer.register({
3425
+ kind: "exact",
3426
+ path: MINIMAX_CAPABILITY_PATH,
3427
+ handler: async (req, res) => {
3428
+ if (!isLoopback(req.headers.host)) {
3429
+ res.writeHead(403, { "Content-Type": "application/json" });
3430
+ res.end(JSON.stringify({ error: "loopback only" }));
3431
+ return;
3432
+ }
3433
+ try {
3434
+ const raw = new URL(req.url ?? "/", "http://127.0.0.1").searchParams.get("report") ?? "";
3435
+ let findings = {};
3436
+ try {
3437
+ const parsed = JSON.parse(raw);
3438
+ if (typeof parsed === "object" && parsed !== null) for (const [key, value] of Object.entries(parsed)) findings[key] = String(value);
3439
+ } catch {
3440
+ findings = { report: "unreadable" };
3441
+ }
3442
+ capability = {
3443
+ at: Date.now(),
3444
+ findings
3445
+ };
3446
+ res.writeHead(204);
3447
+ res.end();
3448
+ } catch (error) {
3449
+ res.writeHead(500, { "Content-Type": "application/json" });
3450
+ res.end(JSON.stringify({ error: error instanceof Error ? error.message : String(error) }));
3451
+ }
3452
+ }
3453
+ }));
3410
3454
  webCtx.effect(() => webCtx.webServer.register({
3411
3455
  kind: "exact",
3412
3456
  path: MINIMAX_GENERATE_PATH,
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "dsh-connect-minimaxcode",
3
3
  "displayName": "DSH Connect MiniMax Code",
4
4
  "description": "把本机已登录的 MiniMax Code 桌面 App 模型接入 DeepSeek Harness:零配置、Anthropic Messages 兼容、动态模型目录,并显示积分余额与到期 — bring MiniMax Code desktop app models into DSH with a dynamic model catalog and a credit balance with expiry details.",
5
- "version": "0.6.2",
5
+ "version": "0.6.4",
6
6
  "author": "JeffTsuiCUG",
7
7
  "contributors": [
8
8
  {