dlinkclaw-openclaw 2026.10.19 → 2026.10.20

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dlinkclaw-openclaw",
3
- "version": "2026.10.19",
3
+ "version": "2026.10.20",
4
4
  "description": "DLinkClaw OpenClaw channel plugin — 朵联 OpenClaw 接入通道",
5
5
  "keywords": [
6
6
  "DLinkClaw",
@@ -28,6 +28,8 @@ export interface MqttConfig {
28
28
  clientTopics: Array<string>;
29
29
  openUserTopics: Array<string>;
30
30
  openUserNotifyTopics: Array<string>;
31
+ /** 2026-10-21:团队创建人 empId(owner-only 能力校验) */
32
+ ownerEmpId?: string;
31
33
  }
32
34
  export declare class DLClient extends EventEmitter {
33
35
  private ctx;
@@ -66,6 +68,8 @@ export declare class DLClient extends EventEmitter {
66
68
  * 而本方法的目的只是刷新 openUserTopics——它是 publishEventToDLClient(L348)实时读取的,
67
69
  * 与 MQTT 订阅目标(clientTopics)、Gateway WS 通道都无关 → 只刷新配置即可,不断连、不重连。
68
70
  */
71
+ /** 2026-10-21:团队创建人(终端等 owner-only 能力使用) */
72
+ getOwnerEmpId(): string | undefined;
69
73
  refreshConfigAndReconnect(): Promise<void>;
70
74
  exeCmd(cmdMessage: CmdMessage): Promise<CmdResponse | undefined>;
71
75
  publishEventToDLClient(payload: any, event: EventType, sqos?: '0' | '1' | '2', to?: string): Promise<void>;
@@ -276,6 +276,10 @@ export class DLClient extends EventEmitter {
276
276
  * 而本方法的目的只是刷新 openUserTopics——它是 publishEventToDLClient(L348)实时读取的,
277
277
  * 与 MQTT 订阅目标(clientTopics)、Gateway WS 通道都无关 → 只刷新配置即可,不断连、不重连。
278
278
  */
279
+ /** 2026-10-21:团队创建人(终端等 owner-only 能力使用) */
280
+ getOwnerEmpId() {
281
+ return this.mqttConfig?.ownerEmpId;
282
+ }
279
283
  async refreshConfigAndReconnect() {
280
284
  try {
281
285
  await this.fetchConfig();
@@ -18,7 +18,10 @@ export class CmdHandler {
18
18
  isInitialized = false;
19
19
  constructor(config, dlClient, logger) {
20
20
  this.dlClient = dlClient;
21
- this.services = new ClawServices(config, logger);
21
+ // 2026-10-21:终端输出经 MQTT 事件回推客户端(协议级能力,品牌无关)
22
+ this.services = new ClawServices(config, logger, (sessionId, data) => {
23
+ void this.dlClient.publishEventToDLClient({ sessionId, data }, 'onTerminal');
24
+ });
22
25
  }
23
26
  log(msg, type) {
24
27
  this.services.log(msg, type);
@@ -216,6 +219,16 @@ export class CmdHandler {
216
219
  else {
217
220
  this.log(`🔄CMD 指令:${rawMessage.cmd}, params=${JSON.stringify(rawMessage)}`, 'info');
218
221
  }
222
+ // 2026-10-21:终端能力仅限团队创建人(owner)。
223
+ // from 是客户端自己的 chat topic:usr_/<empId>/chat,与 ownerEmpId 比对即可。
224
+ if (typeof rawMessage.cmd === 'string' && rawMessage.cmd.startsWith('terminal.')) {
225
+ const owner = this.dlClient.getOwnerEmpId();
226
+ const expected = owner ? `usr_/${owner}/chat` : '';
227
+ if (!owner || rawMessage.from !== expected) {
228
+ this.log(`terminal denied: from=${rawMessage.from}, owner=${owner}`, 'warn');
229
+ return { success: false, error: 'terminal: forbidden (owner only)', timestamp: Date.now() };
230
+ }
231
+ }
219
232
  try {
220
233
  // 调用服务层处理
221
234
  return await this.services.handleCmdMessage(rawMessage);
@@ -5,6 +5,7 @@
5
5
  import EventEmitter from 'events';
6
6
  import { ClawServiceOptions, CmdMessage, CmdResponse } from './types.js';
7
7
  import { PluginLogger } from 'openclaw/plugin-sdk/core';
8
+ import { TerminalDataSink } from './terminal.js';
8
9
  /**
9
10
  * Claw-Services 主类
10
11
  * 处理 CMD 消息并调用相应的 OpenClaw API
@@ -14,7 +15,8 @@ export declare class ClawServices extends EventEmitter {
14
15
  private readonly options;
15
16
  private isInitialized;
16
17
  private readonly logger;
17
- constructor(options: ClawServiceOptions, logger: PluginLogger);
18
+ private readonly terminalManager;
19
+ constructor(options: ClawServiceOptions, logger: PluginLogger, terminalSink?: TerminalDataSink);
18
20
  log(msg: string, type: 'info' | 'warn' | 'error' | 'debug'): void;
19
21
  /**
20
22
  * 初始化服务
@@ -17,6 +17,7 @@ import { getSessionEntry, patchSessionEntry, resolveStorePath, listSessionEntrie
17
17
  import { setGatewayClient, approvePairingRequest } from './gateway-rpc.js';
18
18
  import { DEFAULT_ACCOUNT_ID } from 'openclaw/plugin-sdk/core';
19
19
  import { markRunStopped } from '../runtime.js';
20
+ import { TerminalManager } from './terminal.js';
20
21
  /**
21
22
  * Claw-Services 主类
22
23
  * 处理 CMD 消息并调用相应的 OpenClaw API
@@ -26,10 +27,13 @@ export class ClawServices extends EventEmitter {
26
27
  options;
27
28
  isInitialized = false;
28
29
  logger;
29
- constructor(options, logger) {
30
+ // 2026-10-21:终端会话管理(PTY / 管道)
31
+ terminalManager;
32
+ constructor(options, logger, terminalSink) {
30
33
  super();
31
34
  this.options = options;
32
35
  this.logger = logger;
36
+ this.terminalManager = new TerminalManager(terminalSink ?? (() => { }), (msg, type) => this.log(msg, type ?? 'info'));
33
37
  }
34
38
  log(msg, type) {
35
39
  switch (type) {
@@ -266,6 +270,19 @@ export class ClawServices extends EventEmitter {
266
270
  // 新增 cmd 通道 chat.abort , 以上两个/stop, /queue interrupt都不能立即终止执行中的会话
267
271
  result = await this.handlChatAbout(cmdMessage.params);
268
272
  break;
273
+ // 2026-10-21:终端能力(owner-only,见 CmdHandler 校验)
274
+ case 'terminal.open':
275
+ result = await this.terminalManager.open(cmdMessage.params);
276
+ break;
277
+ case 'terminal.input':
278
+ result = this.terminalManager.write(cmdMessage.params?.sessionId, cmdMessage.params?.data ?? '');
279
+ break;
280
+ case 'terminal.resize':
281
+ result = this.terminalManager.resize(cmdMessage.params?.sessionId, cmdMessage.params?.cols, cmdMessage.params?.rows);
282
+ break;
283
+ case 'terminal.close':
284
+ result = this.terminalManager.close(cmdMessage.params?.sessionId);
285
+ break;
269
286
  case 'pairing.approve':
270
287
  // 2026-09-06 适配 openclaw 2026.9.1:审批改按 requestId(宿主 RPC),客户端由 onPairingRequest 事件获取;
271
288
  // code 保留解析用于旧客户端兼容提示(9.1 起宿主 list 不再暴露 code,无法按 code 审批)
@@ -330,10 +347,19 @@ export class ClawServices extends EventEmitter {
330
347
  * @param userId
331
348
  */
332
349
  async handleUpdateSessionModel(params) {
333
- const { sessionKey, provider, model } = params;
334
- if (!provider || provider.length === 0 || !sessionKey || sessionKey.length === 0) {
350
+ const { sessionKey, provider, model, thinkingLevel } = params;
351
+ if (!sessionKey || sessionKey.length === 0) {
352
+ return null;
353
+ }
354
+ // 2026-10-21:支持「只改推理强度」的 patch;provider/model 与 thinkingLevel 至少给一个
355
+ if ((!provider || provider.length === 0 || !model || model.length === 0) && !thinkingLevel) {
335
356
  return null;
336
357
  }
358
+ // 推理强度合法值校验(off/minimal/low/medium/high),非法值直接忽略
359
+ const THINK_LEVELS = ['off', 'minimal', 'low', 'medium', 'high'];
360
+ const thinkOverride = thinkingLevel && THINK_LEVELS.includes(thinkingLevel)
361
+ ? thinkingLevel
362
+ : undefined;
337
363
  const agentId = sessionKey.split(':')[1];
338
364
  const { cfg, channelId } = this.options;
339
365
  const storePath = resolveStorePath(cfg.session?.store, { agentId });
@@ -346,19 +372,25 @@ export class ClawServices extends EventEmitter {
346
372
  storePath,
347
373
  sessionKey: realKey,
348
374
  update: (entry) => {
349
- applyModelOverrideToSessionEntry({
350
- entry,
351
- selection: {
352
- provider: provider,
353
- model: model,
354
- },
355
- selectionSource: 'user',
356
- markLiveSwitchPending: true, // 让当前 run 立即生效
357
- });
375
+ if (provider && model) {
376
+ applyModelOverrideToSessionEntry({
377
+ entry,
378
+ selection: {
379
+ provider: provider,
380
+ model: model,
381
+ },
382
+ selectionSource: 'user',
383
+ markLiveSwitchPending: true, // 让当前 run 立即生效
384
+ });
385
+ }
386
+ // 2026-10-21:会话级推理强度写入(与模型覆盖同层持久化)
387
+ if (thinkOverride) {
388
+ entry.thinkingLevel = thinkOverride;
389
+ }
358
390
  return { ...entry };
359
391
  },
360
392
  });
361
- return { provider, model };
393
+ return { provider, model, thinkingLevel: thinkOverride };
362
394
  }
363
395
  /**
364
396
  * 2026-08-28 解析 direct 会话的真实 sessionKey(兼容 chatId 后缀)
@@ -417,13 +449,16 @@ export class ClawServices extends EventEmitter {
417
449
  if (!entry)
418
450
  return null;
419
451
  // 只返回用户主动设置的覆盖,不返回 auto fallback
452
+ const result = {};
420
453
  if (entry.modelOverrideSource === 'user' && entry.modelOverride) {
421
- return {
422
- provider: entry.providerOverride ?? '',
423
- model: entry.modelOverride,
424
- };
454
+ result.provider = entry.providerOverride ?? '';
455
+ result.model = entry.modelOverride;
456
+ }
457
+ // 2026-10-21:会话级推理强度回显
458
+ if (entry.thinkingLevel) {
459
+ result.thinkingLevel = entry.thinkingLevel;
425
460
  }
426
- return null;
461
+ return Object.keys(result).length > 0 ? result : null;
427
462
  }
428
463
  //#region ==================== Agent 管理处理器 ====================
429
464
  async handleAgentList() {
@@ -0,0 +1,39 @@
1
+ export type TerminalDataSink = (sessionId: string, data: string) => void;
2
+ export type TerminalLogger = (msg: string, type?: 'info' | 'warn' | 'error' | 'debug') => void;
3
+ export declare class TerminalManager {
4
+ private readonly sink;
5
+ private readonly log;
6
+ private readonly sessions;
7
+ private ptyModule;
8
+ constructor(sink: TerminalDataSink, log: TerminalLogger);
9
+ private defaultCwd;
10
+ private append;
11
+ private touch;
12
+ open(params: {
13
+ sessionId?: string;
14
+ cwd?: string;
15
+ cols?: number;
16
+ rows?: number;
17
+ }): Promise<{
18
+ sessionId: string;
19
+ mode: string;
20
+ reused: boolean;
21
+ replay: string;
22
+ } | {
23
+ sessionId: string;
24
+ mode: string;
25
+ reused?: undefined;
26
+ replay?: undefined;
27
+ }>;
28
+ write(sessionId: string, data: string): {
29
+ sessionId: string;
30
+ };
31
+ resize(sessionId: string, cols: number, rows: number): {
32
+ sessionId: string;
33
+ };
34
+ close(sessionId: string, alreadyExited?: boolean): {
35
+ sessionId: string;
36
+ closed: boolean;
37
+ };
38
+ disposeAll(): void;
39
+ }
@@ -0,0 +1,217 @@
1
+ /**
2
+ * 2026-10-21:终端能力(协议级,非 OpenClaw 专属)。
3
+ * 通过 PTY(可用时)或 child_process 运行登录 shell,输出经 sink 回推客户端。
4
+ * 其他品牌插件(如 dsh)实现同一套 terminal.* 契约即可。
5
+ */
6
+ import { spawn } from 'child_process';
7
+ import os from 'os';
8
+ import path from 'path';
9
+ const FLUSH_MS = 16;
10
+ const IDLE_MS = 30 * 60 * 1000;
11
+ /** 重新 attach 时回放的最大输出量 */
12
+ const HISTORY_LIMIT = 64 * 1024;
13
+ /** 优先用 PTY(交互式 shell),不可用时退回管道模式 */
14
+ async function loadPty() {
15
+ for (const name of ['node-pty', '@lydell/node-pty']) {
16
+ try {
17
+ const mod = await import(name);
18
+ if (mod) {
19
+ return mod.default ?? mod;
20
+ }
21
+ }
22
+ catch {
23
+ // 继续尝试下一个
24
+ }
25
+ }
26
+ return null;
27
+ }
28
+ export class TerminalManager {
29
+ sink;
30
+ log;
31
+ sessions = new Map();
32
+ ptyModule;
33
+ constructor(sink, log) {
34
+ this.sink = sink;
35
+ this.log = log;
36
+ }
37
+ defaultCwd() {
38
+ return path.join(os.homedir(), '.openclaw', 'workspace');
39
+ }
40
+ append(session, chunk) {
41
+ if (session.closed || !chunk) {
42
+ return;
43
+ }
44
+ session.buffer += chunk;
45
+ if (session.history.length < HISTORY_LIMIT) {
46
+ session.history += chunk;
47
+ if (session.history.length > HISTORY_LIMIT) {
48
+ session.history = session.history.slice(-HISTORY_LIMIT);
49
+ }
50
+ }
51
+ if (session.flushTimer) {
52
+ return;
53
+ }
54
+ // 16ms 合并,避免高频输出打爆 MQTT
55
+ session.flushTimer = setTimeout(() => {
56
+ session.flushTimer = null;
57
+ const data = session.buffer;
58
+ session.buffer = '';
59
+ if (data) {
60
+ try {
61
+ this.sink(session.id, data);
62
+ }
63
+ catch (e) {
64
+ this.log(`terminal sink failed: ${String(e)}`, 'warn');
65
+ }
66
+ }
67
+ }, FLUSH_MS);
68
+ }
69
+ touch(session) {
70
+ if (session.idleTimer) {
71
+ clearTimeout(session.idleTimer);
72
+ }
73
+ session.idleTimer = setTimeout(() => {
74
+ this.log(`terminal session idle timeout: ${session.id}`, 'info');
75
+ this.close(session.id);
76
+ }, IDLE_MS);
77
+ }
78
+ async open(params) {
79
+ const sessionId = params.sessionId;
80
+ if (!sessionId) {
81
+ throw new Error('terminal.open: sessionId required');
82
+ }
83
+ const existing = this.sessions.get(sessionId);
84
+ if (existing) {
85
+ // 2026-10-21:客户端切 Tab 后重新 attach,回放最近输出,保持会话连续
86
+ this.touch(existing);
87
+ return {
88
+ sessionId,
89
+ mode: existing.isPty ? 'pty' : 'pipe',
90
+ reused: true,
91
+ replay: existing.history,
92
+ };
93
+ }
94
+ if (this.ptyModule === undefined) {
95
+ this.ptyModule = await loadPty();
96
+ if (!this.ptyModule) {
97
+ this.log('node-pty unavailable, fallback to pipe mode (no TTY)', 'warn');
98
+ }
99
+ }
100
+ const cwd = params.cwd && params.cwd.length > 0 ? params.cwd : this.defaultCwd();
101
+ const cols = params.cols && params.cols > 0 ? params.cols : 120;
102
+ const rows = params.rows && params.rows > 0 ? params.rows : 30;
103
+ const env = { ...process.env, TERM: 'xterm-256color', LANG: process.env.LANG ?? 'C.UTF-8' };
104
+ let proc;
105
+ let isPty = false;
106
+ if (this.ptyModule?.spawn) {
107
+ proc = this.ptyModule.spawn('bash', ['-il'], {
108
+ name: 'xterm-256color',
109
+ cols,
110
+ rows,
111
+ cwd,
112
+ env,
113
+ });
114
+ isPty = true;
115
+ }
116
+ else {
117
+ proc = spawn('bash', ['-il'], { cwd, env, stdio: 'pipe' });
118
+ isPty = false;
119
+ }
120
+ const session = {
121
+ id: sessionId,
122
+ proc,
123
+ isPty,
124
+ buffer: '',
125
+ history: '',
126
+ flushTimer: null,
127
+ idleTimer: null,
128
+ closed: false,
129
+ };
130
+ this.sessions.set(sessionId, session);
131
+ this.touch(session);
132
+ const onData = (data) => {
133
+ this.touch(session);
134
+ this.append(session, typeof data === 'string' ? data : data.toString('utf8'));
135
+ };
136
+ if (isPty) {
137
+ proc.onData(onData);
138
+ proc.onExit((e) => {
139
+ this.log(`terminal exited: ${sessionId}, code=${e?.exitCode}`, 'info');
140
+ this.close(sessionId, true);
141
+ });
142
+ }
143
+ else {
144
+ proc.stdout?.on('data', onData);
145
+ proc.stderr?.on('data', onData);
146
+ proc.on('exit', (code) => {
147
+ this.log(`terminal exited: ${sessionId}, code=${code}`, 'info');
148
+ this.close(sessionId, true);
149
+ });
150
+ proc.on('error', (err) => {
151
+ this.log(`terminal error: ${sessionId}, ${err.message}`, 'error');
152
+ this.append(session, `\r\n[terminal error] ${err.message}\r\n`);
153
+ });
154
+ }
155
+ this.log(`terminal opened: ${sessionId}, mode=${isPty ? 'pty' : 'pipe'}, cwd=${cwd}`, 'info');
156
+ return { sessionId, mode: isPty ? 'pty' : 'pipe' };
157
+ }
158
+ write(sessionId, data) {
159
+ const session = this.sessions.get(sessionId);
160
+ if (!session || session.closed) {
161
+ throw new Error('terminal session not found');
162
+ }
163
+ this.touch(session);
164
+ if (session.isPty) {
165
+ session.proc.write(data);
166
+ }
167
+ else {
168
+ session.proc.stdin?.write(data);
169
+ }
170
+ return { sessionId };
171
+ }
172
+ resize(sessionId, cols, rows) {
173
+ const session = this.sessions.get(sessionId);
174
+ if (!session || session.closed) {
175
+ throw new Error('terminal session not found');
176
+ }
177
+ if (session.isPty && session.proc.resize) {
178
+ session.proc.resize(cols, rows);
179
+ }
180
+ return { sessionId };
181
+ }
182
+ close(sessionId, alreadyExited = false) {
183
+ const session = this.sessions.get(sessionId);
184
+ if (!session) {
185
+ return { sessionId, closed: false };
186
+ }
187
+ session.closed = true;
188
+ if (session.flushTimer) {
189
+ clearTimeout(session.flushTimer);
190
+ session.flushTimer = null;
191
+ }
192
+ if (session.idleTimer) {
193
+ clearTimeout(session.idleTimer);
194
+ session.idleTimer = null;
195
+ }
196
+ if (!alreadyExited) {
197
+ try {
198
+ if (session.isPty) {
199
+ session.proc.kill();
200
+ }
201
+ else {
202
+ session.proc.kill('SIGHUP');
203
+ }
204
+ }
205
+ catch {
206
+ // 忽略
207
+ }
208
+ }
209
+ this.sessions.delete(sessionId);
210
+ return { sessionId, closed: true };
211
+ }
212
+ disposeAll() {
213
+ for (const id of Array.from(this.sessions.keys())) {
214
+ this.close(id);
215
+ }
216
+ }
217
+ }
@@ -306,7 +306,7 @@ export interface CmdResult {
306
306
  timestamp: string | number;
307
307
  timezone: string;
308
308
  }
309
- export type CmdType = 'agent.ask' | 'agent.list' | 'agent.create' | 'agent.update' | 'agent.delete' | 'agent.get' | 'agent.stop' | 'chat.abort' | 'task.list' | 'task.create' | 'task.update' | 'task.delete' | 'task.run' | 'status.health' | 'status.system' | 'status.usage' | 'models.list' | 'sessions.list' | 'sessions.get' | 'sessions.describe' | 'sessions.resolve' | 'sessions.create' | 'sessions.archive' | 'sessions.restore' | 'sessions.delete' | 'sessions.reset' | 'sessions.patch' | 'sessions.currentModel' | 'chat.history' | 'chat.trajectory' | 'skills.status' | 'skills.search' | 'skills.detail' | 'skills.install' | 'skills.update' | 'skills.securityVerdicts' | 'cancelMessage' | 'usage.cost' | 'sessions.usage' | 'sessions.usage.logs' | 'pairing.approve' | 'artifacts.list' | 'artifacts.get' | 'artifacts.download' | 'agents.files.list' | 'agents.files.get' | 'extCapabilities.status' | 'extCapabilities.check' | 'extCapabilities.install';
309
+ export type CmdType = 'agent.ask' | 'agent.list' | 'agent.create' | 'agent.update' | 'agent.delete' | 'agent.get' | 'agent.stop' | 'chat.abort' | 'task.list' | 'task.create' | 'task.update' | 'task.delete' | 'task.run' | 'status.health' | 'status.system' | 'status.usage' | 'models.list' | 'sessions.list' | 'sessions.get' | 'sessions.describe' | 'sessions.resolve' | 'sessions.create' | 'sessions.archive' | 'sessions.restore' | 'sessions.delete' | 'sessions.reset' | 'sessions.patch' | 'sessions.currentModel' | 'terminal.open' | 'terminal.input' | 'terminal.resize' | 'terminal.close' | 'chat.history' | 'chat.trajectory' | 'skills.status' | 'skills.search' | 'skills.detail' | 'skills.install' | 'skills.update' | 'skills.securityVerdicts' | 'cancelMessage' | 'usage.cost' | 'sessions.usage' | 'sessions.usage.logs' | 'pairing.approve' | 'artifacts.list' | 'artifacts.get' | 'artifacts.download' | 'agents.files.list' | 'agents.files.get' | 'extCapabilities.status' | 'extCapabilities.check' | 'extCapabilities.install';
310
310
  export type ReadlineType = Extract<CmdType, 'chat.trajectory'>;
311
311
  export interface ClawMessage {
312
312
  type: 'res' | 'event';
@@ -316,11 +316,13 @@ export interface ClawMessage {
316
316
  payload: any;
317
317
  error?: any;
318
318
  }
319
- export type EventType = 'onTick' | 'onHealth' | 'onHandshaked' | 'onConnectState' | 'onError' | 'onAgentReply' | 'onAgentChat' | 'onChatTrajectory' | 'onCron' | 'onShutdown' | 'onSessionsChanged' | 'onSkillInstalled' | 'onSkillInstalling' | 'onSkillError' | 'onPairingRequest' | 'onPairingResult';
319
+ export type EventType = 'onTick' | 'onHealth' | 'onHandshaked' | 'onConnectState' | 'onError' | 'onAgentReply' | 'onAgentChat' | 'onChatTrajectory' | 'onCron' | 'onShutdown' | 'onSessionsChanged' | 'onSkillInstalled' | 'onSkillInstalling' | 'onSkillError' | 'onPairingRequest' | 'onPairingResult' | 'onTerminal';
320
320
  export type CmdParamsPatchSessionEntry = {
321
321
  sessionKey: string;
322
322
  provider?: string;
323
- model: string;
323
+ model?: string;
324
+ /** 2026-10-21:会话级推理强度(off/minimal/low/medium/high),与模型选择同通道 */
325
+ thinkingLevel?: string;
324
326
  };
325
327
  export interface SkillInfo {
326
328
  name: string;
@@ -390,3 +392,21 @@ export interface AgentFilesGetParams {
390
392
  agentId: string;
391
393
  name: string;
392
394
  }
395
+ export type TerminalOpenParams = {
396
+ sessionId: string;
397
+ cwd?: string;
398
+ cols?: number;
399
+ rows?: number;
400
+ };
401
+ export type TerminalInputParams = {
402
+ sessionId: string;
403
+ data: string;
404
+ };
405
+ export type TerminalResizeParams = {
406
+ sessionId: string;
407
+ cols: number;
408
+ rows: number;
409
+ };
410
+ export type TerminalCloseParams = {
411
+ sessionId: string;
412
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dlinkclaw-openclaw",
3
- "version": "2026.10.19",
3
+ "version": "2026.10.20",
4
4
  "description": "DLinkClaw OpenClaw channel plugin — 朵联 OpenClaw 接入通道",
5
5
  "keywords": [
6
6
  "DLinkClaw",