openclaw-linso 1.0.9 → 1.0.11

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/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import { setLinsoRuntime } from "./src/runtime.js";
7
7
  export { monitorLinsoProvider } from "./src/monitor.js";
8
8
  export { linsoPlugin } from "./src/channel.js";
9
9
  const plugin = {
10
- id: "linso",
10
+ id: "openclaw-linso",
11
11
  name: "Linso iOS Channel",
12
12
  description: "Linso iOS App WebSocket 直连渠道,与飞书接入方式完全一致",
13
13
  register(api) {
@@ -158,11 +158,13 @@ function handleSlashCommand(raw, deviceId, cfg) {
158
158
  const name = args[0].toLowerCase();
159
159
  if (name === "off" || name === "default" || name === "reset") {
160
160
  deviceModelOverrides.delete(deviceId);
161
+ patchSessionModelInStore(deviceId, null, cfg);
161
162
  return { kind: "sync", text: "✅ 已恢复默认模型" };
162
163
  }
163
- // 没有 / 时自动补全默认 provider
164
- const modelInput = args[0].includes("/") ? args[0] : `${DEFAULT_MODEL_PROVIDER}/${args[0]}`;
164
+ // 没有 / 时,从 cfg 里查找正确的 provider,找不到才用默认
165
+ const modelInput = args[0].includes("/") ? args[0] : resolveModelProvider(args[0], cfg);
165
166
  deviceModelOverrides.set(deviceId, modelInput);
167
+ patchSessionModelInStore(deviceId, modelInput, cfg);
166
168
  return { kind: "sync", text: `✅ 模型已切换为:${modelInput}` };
167
169
  }
168
170
  // ── 状态信息 ──────────────────────────────────────────────────────────────
@@ -391,8 +393,65 @@ async function handleIncomingMessage(deviceId, text, imageUrls, cfg, log) {
391
393
  }
392
394
  // ── 工具函数 ──────────────────────────────────────────────────────────────────
393
395
  /**
394
- * 读取 sessions.json 并格式化状态卡片。
396
+ * cfg.models.providers 里查找 modelId 属于哪个 provider。
397
+ * 找到返回 "provider/modelId",找不到返回 "DEFAULT_MODEL_PROVIDER/modelId"。
395
398
  */
399
+ function resolveModelProvider(modelId, cfg) {
400
+ const providers = cfg?.models;
401
+ const providerMap = providers?.providers;
402
+ if (providerMap) {
403
+ for (const [providerName, providerCfg] of Object.entries(providerMap)) {
404
+ const models = providerCfg?.models ?? [];
405
+ if (models.some((m) => m.id === modelId)) {
406
+ return `${providerName}/${modelId}`;
407
+ }
408
+ }
409
+ }
410
+ return `${DEFAULT_MODEL_PROVIDER}/${modelId}`;
411
+ }
412
+ /**
413
+ * 直接把 modelOverride / providerOverride 写进 sessions.json,
414
+ * 这样下次 dispatchReplyFromConfig 读 session entry 时就能生效。
415
+ * (session entry 里的 modelOverride 优先级高于 cfg.agents.defaults.model)
416
+ */
417
+ function patchSessionModelInStore(deviceId, modelOverride, cfg) {
418
+ try {
419
+ const core = getLinsoRuntime();
420
+ const peerId = getDevicePeerId(deviceId);
421
+ const route = core.channel.routing.resolveAgentRoute({
422
+ cfg,
423
+ channel: "linso",
424
+ accountId: "default",
425
+ peer: { kind: "direct", id: peerId },
426
+ });
427
+ const agentId = route.agentId ?? "main";
428
+ const sessionKey = route.sessionKey;
429
+ const store = loadSessionsStore(agentId);
430
+ const entry = store[sessionKey] ?? {};
431
+ if (!modelOverride) {
432
+ // 恢复默认:清除 override
433
+ delete entry.modelOverride;
434
+ delete entry.providerOverride;
435
+ }
436
+ else {
437
+ const slashIdx = modelOverride.indexOf("/");
438
+ if (slashIdx !== -1) {
439
+ entry.providerOverride = modelOverride.slice(0, slashIdx);
440
+ entry.modelOverride = modelOverride.slice(slashIdx + 1);
441
+ }
442
+ else {
443
+ entry.modelOverride = modelOverride;
444
+ delete entry.providerOverride;
445
+ }
446
+ }
447
+ store[sessionKey] = entry;
448
+ saveSessionsStore(agentId, store);
449
+ }
450
+ catch (e) {
451
+ // 非致命,不影响正常消息流
452
+ console.error("[Linso] patchSessionModelInStore failed:", e);
453
+ }
454
+ }
396
455
  async function buildStatusCard(deviceId, cfg) {
397
456
  const core = getLinsoRuntime();
398
457
  const peerId = getDevicePeerId(deviceId);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-linso",
3
- "version": "1.0.9",
3
+ "version": "1.0.11",
4
4
  "description": "Linso iOS App channel plugin for OpenClaw — connects via cloud Relay Server",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/src/monitor.ts CHANGED
@@ -204,11 +204,13 @@ function handleSlashCommand(raw: string, deviceId: string, cfg: OpenClawConfig):
204
204
  const name = args[0].toLowerCase();
205
205
  if (name === "off" || name === "default" || name === "reset") {
206
206
  deviceModelOverrides.delete(deviceId);
207
+ patchSessionModelInStore(deviceId, null, cfg);
207
208
  return { kind: "sync", text: "✅ 已恢复默认模型" };
208
209
  }
209
- // 没有 / 时自动补全默认 provider
210
- const modelInput = args[0].includes("/") ? args[0] : `${DEFAULT_MODEL_PROVIDER}/${args[0]}`;
210
+ // 没有 / 时,从 cfg 里查找正确的 provider,找不到才用默认
211
+ const modelInput = args[0].includes("/") ? args[0] : resolveModelProvider(args[0], cfg);
211
212
  deviceModelOverrides.set(deviceId, modelInput);
213
+ patchSessionModelInStore(deviceId, modelInput, cfg);
212
214
  return { kind: "sync", text: `✅ 模型已切换为:${modelInput}` };
213
215
  }
214
216
 
@@ -466,8 +468,67 @@ async function handleIncomingMessage(
466
468
  // ── 工具函数 ──────────────────────────────────────────────────────────────────
467
469
 
468
470
  /**
469
- * 读取 sessions.json 并格式化状态卡片。
471
+ * cfg.models.providers 里查找 modelId 属于哪个 provider。
472
+ * 找到返回 "provider/modelId",找不到返回 "DEFAULT_MODEL_PROVIDER/modelId"。
470
473
  */
474
+ function resolveModelProvider(modelId: string, cfg: OpenClawConfig): string {
475
+ const providers = (cfg as unknown as Record<string, unknown>)?.models as Record<string, unknown>;
476
+ const providerMap = providers?.providers as Record<string, { models?: Array<{ id: string }> }> | undefined;
477
+ if (providerMap) {
478
+ for (const [providerName, providerCfg] of Object.entries(providerMap)) {
479
+ const models = providerCfg?.models ?? [];
480
+ if (models.some((m) => m.id === modelId)) {
481
+ return `${providerName}/${modelId}`;
482
+ }
483
+ }
484
+ }
485
+ return `${DEFAULT_MODEL_PROVIDER}/${modelId}`;
486
+ }
487
+
488
+ /**
489
+ * 直接把 modelOverride / providerOverride 写进 sessions.json,
490
+ * 这样下次 dispatchReplyFromConfig 读 session entry 时就能生效。
491
+ * (session entry 里的 modelOverride 优先级高于 cfg.agents.defaults.model)
492
+ */
493
+ function patchSessionModelInStore(deviceId: string, modelOverride: string | null, cfg: OpenClawConfig): void {
494
+ try {
495
+ const core = getLinsoRuntime();
496
+ const peerId = getDevicePeerId(deviceId);
497
+ const route = core.channel.routing.resolveAgentRoute({
498
+ cfg,
499
+ channel: "linso",
500
+ accountId: "default",
501
+ peer: { kind: "direct", id: peerId },
502
+ });
503
+ const agentId = route.agentId ?? "main";
504
+ const sessionKey = route.sessionKey;
505
+ const store = loadSessionsStore(agentId);
506
+ const entry: Record<string, unknown> = (store[sessionKey] as Record<string, unknown>) ?? {};
507
+
508
+ if (!modelOverride) {
509
+ // 恢复默认:清除 override
510
+ delete entry.modelOverride;
511
+ delete entry.providerOverride;
512
+ } else {
513
+ const slashIdx = modelOverride.indexOf("/");
514
+ if (slashIdx !== -1) {
515
+ entry.providerOverride = modelOverride.slice(0, slashIdx);
516
+ entry.modelOverride = modelOverride.slice(slashIdx + 1);
517
+ } else {
518
+ entry.modelOverride = modelOverride;
519
+ delete entry.providerOverride;
520
+ }
521
+ }
522
+
523
+ store[sessionKey] = entry;
524
+ saveSessionsStore(agentId, store);
525
+ } catch (e) {
526
+ // 非致命,不影响正常消息流
527
+ console.error("[Linso] patchSessionModelInStore failed:", e);
528
+ }
529
+ }
530
+
531
+
471
532
  async function buildStatusCard(deviceId: string, cfg: OpenClawConfig): Promise<string> {
472
533
  const core = getLinsoRuntime();
473
534
  const peerId = getDevicePeerId(deviceId);