@zhin.js/adapter 1.1.5 → 1.1.8

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.
@@ -11,13 +11,18 @@ export type EndpointCommandReply = (text: string) => Promise<unknown>;
11
11
  * 从命令 input(Runtime Message)提取 $reply;非消息来源(如 Host API 调用)降级为 no-op。
12
12
  */
13
13
  export declare function extractEndpointCommandReply(input: unknown): EndpointCommandReply;
14
+ /**
15
+ * bindFlow 后续状态推送:优先走 OutboundHost(不受 inbound Message reply scope 限制)。
16
+ * 扫码绑定等长流程会在命令结果已送达、`$reply` 已冻结后继续 notify,必须用 durable 出站。
17
+ */
18
+ export declare function createDurableEndpointCommandReply(input: unknown, use: EndpointCommandUse): EndpointCommandReply;
14
19
  export interface EndpointRunningInfo {
15
- readonly name: string;
20
+ readonly id: string;
16
21
  /** 连接模式(ws / wss / polling / socket-mode …),仅用于 list 展示。 */
17
22
  readonly mode?: string;
18
23
  }
19
24
  export interface EndpointRuntimeState {
20
- /** 当前 generation 已成功创建的 endpoint(name → 描述) */
25
+ /** 当前 generation 已成功创建的 endpoint(id → 描述) */
21
26
  readonly endpoints: Map<string, EndpointRunningInfo>;
22
27
  }
23
28
  export declare function createEndpointRuntimeState(): EndpointRuntimeState;
@@ -26,21 +31,21 @@ export declare function defineEndpointRuntimeStateToken(adapterKey: string): Tok
26
31
  /** 项目根:ZHIN_PROJECT_ROOT 优先,缺省 process.cwd()(替代 legacy runtimeCwd) */
27
32
  export declare function resolveProjectRoot(): string;
28
33
  /** 派生 endpoint 凭据的 env 键:`${ADAPTER}_${NAME}_${FIELD}`(如 `TELEGRAM_MY_BOT_TOKEN`) */
29
- export declare function buildEndpointEnvKey(adapterKey: string, endpointName: string, fieldKey: string): string;
34
+ export declare function buildEndpointEnvKey(adapterKey: string, endpointId: string, fieldKey: string): string;
30
35
  /** 写入或更新 `.env` 中的键值,并同步到当前进程 `process.env` */
31
36
  export declare function persistEndpointEnvValues(values: Readonly<Record<string, string>>, projectRoot?: string): void;
32
37
  export interface ConfiguredEndpointEntry {
33
- name: string;
38
+ id: string;
34
39
  [key: string]: unknown;
35
40
  }
36
41
  /** 定位项目配置文件:ZHIN_CONFIG 指定优先,否则发现 zhin.config.yml/.yaml,都没有则默认新建 zhin.config.yml */
37
42
  export declare function findEndpointConfigFile(adapterKey: string, projectRoot?: string): string;
38
43
  /** 读取 plugins.<adapterKey>.endpoints(plain JS);plugins/<adapterKey> 缺失或形态不符时返回 [] */
39
44
  export declare function listConfiguredEndpoints(adapterKey: string, projectRoot?: string): ConfiguredEndpointEntry[];
40
- /** 追加 endpoint 到 plugins.<adapterKey>.endpoints;name 已存在时报错 */
45
+ /** 追加 endpoint 到 plugins.<adapterKey>.endpoints;id 已存在时报错 */
41
46
  export declare function addEndpointToConfig(adapterKey: string, entry: ConfiguredEndpointEntry, projectRoot?: string): string;
42
- /** 按 name 移除 plugins.<adapterKey>.endpoints 项;不存在返回 false */
43
- export declare function removeEndpointFromConfig(adapterKey: string, name: string, projectRoot?: string): {
47
+ /** 按 id 移除 plugins.<adapterKey>.endpoints 项;不存在返回 false */
48
+ export declare function removeEndpointFromConfig(adapterKey: string, id: string, projectRoot?: string): {
44
49
  removed: boolean;
45
50
  filePath: string;
46
51
  };
@@ -58,9 +63,9 @@ export interface EndpointFieldSpec {
58
63
  export type EndpointCommandUse = <T>(token: Token<T>) => T;
59
64
  /** bindFlow 钩子上下文:接管 add 命令的自定义绑定流程(如 QQ 扫码)。 */
60
65
  export interface EndpointBindFlowContext {
61
- /** 命令参数 name(未指定时为 undefined,流程可自行决定终名) */
62
- readonly name?: string;
63
- /** 向当前会话推送后续状态(二维码刷新 / 成功 / 失败) */
66
+ /** 命令参数 id(未指定时为 undefined,流程可自行决定终名) */
67
+ readonly id?: string;
68
+ /** 向当前会话推送后续状态(二维码刷新 / 成功 / 失败;走 durable OutboundHost,可在命令 reply scope 结束后调用) */
64
69
  readonly reply: EndpointCommandReply;
65
70
  readonly config: unknown;
66
71
  readonly input: unknown;
@@ -126,8 +131,8 @@ export declare function formatEndpointList(spec: Pick<EndpointCommandsSpec, 'ada
126
131
  readonly footer?: string;
127
132
  }): string;
128
133
  /** add(kv 模式)的完整业务逻辑:解析 kv → 凭据写 .env → 追加 yaml;返回回复文本。 */
129
- export declare function addEndpointFromKeyValues(spec: EndpointCommandsSpec, name: string, args: readonly string[], projectRoot?: string): string;
134
+ export declare function addEndpointFromKeyValues(spec: EndpointCommandsSpec, id: string, args: readonly string[], projectRoot?: string): string;
130
135
  /** remove 的完整业务逻辑:从 yaml 移除;返回回复文本。 */
131
- export declare function removeEndpointByName(spec: Pick<EndpointCommandsSpec, 'adapterKey'>, name: string, projectRoot?: string): string;
136
+ export declare function removeEndpointById(spec: Pick<EndpointCommandsSpec, 'adapterKey'>, id: string, projectRoot?: string): string;
132
137
  /** 生成 `<adapter> endpoint` 的 list / add / remove 三个命令定义(见文件头接入步骤)。 */
133
138
  export declare function createEndpointCommands<TCommand>(spec: EndpointCommandsSpec, defineCommand: (definition: EndpointCommandDefinition) => TCommand): EndpointCommands<TCommand>;
@@ -16,7 +16,7 @@
16
16
  * 接入步骤(以 telegram 为例):
17
17
  * 1. plugin.ts setup 里 `context.resources.provide(telegramRuntimeStateToken, createEndpointRuntimeState())`,
18
18
  * token 由 `defineEndpointRuntimeStateToken('telegram')` 创建。
19
- * 2. adapters/telegram.ts create() 里 `context.use(token).endpoints.set(config.name, { name, mode })`。
19
+ * 2. adapters/telegram.ts create() 里 `context.use(token).endpoints.set(config.id, { id, mode })`。
20
20
  * 3. src 下 `export const telegramEndpointCommands = createEndpointCommands({ adapterKey: 'telegram', ... }, defineCommand)`
21
21
  * (defineCommand 由调用方从 @zhin.js/command 传入——provider 包之间禁止互相 import,
22
22
  * 见 scripts/check-architecture-layers.mjs,故 defineCommand 走依赖注入)。
@@ -28,7 +28,7 @@
28
28
  */
29
29
  import fs from 'node:fs';
30
30
  import path from 'node:path';
31
- import { createToken } from '@zhin.js/plugin-runtime';
31
+ import { createToken, outboundHostToken, } from '@zhin.js/plugin-runtime';
32
32
  import { isMap, isSeq, parseDocument } from 'yaml';
33
33
  // ---------------------------------------------------------------------------
34
34
  // 权限:master 判定
@@ -55,8 +55,9 @@ export function isEndpointOperator(config, input) {
55
55
  }
56
56
  if (masters.size === 0)
57
57
  return true;
58
- const sender = String(input?.sender ?? '').trim();
59
- return !!sender && masters.has(sender);
58
+ const senderRaw = input?.sender;
59
+ const senderId = (typeof senderRaw === 'object' && senderRaw?.id) ? senderRaw.id.trim() : '';
60
+ return !!senderId && masters.has(senderId);
60
61
  }
61
62
  /** add/remove 的拒绝文案(list 只读,不校验)。 */
62
63
  export function endpointCommandForbidden(adapterDisplayName) {
@@ -72,6 +73,56 @@ export function extractEndpointCommandReply(input) {
72
73
  }
73
74
  return async () => undefined;
74
75
  }
76
+ /**
77
+ * bindFlow 后续状态推送:优先走 OutboundHost(不受 inbound Message reply scope 限制)。
78
+ * 扫码绑定等长流程会在命令结果已送达、`$reply` 已冻结后继续 notify,必须用 durable 出站。
79
+ */
80
+ export function createDurableEndpointCommandReply(input, use) {
81
+ const scoped = extractEndpointCommandReply(input);
82
+ const target = readOutboundSendTarget(input);
83
+ if (!target)
84
+ return scoped;
85
+ return async (text) => {
86
+ let outbound;
87
+ try {
88
+ outbound = use(outboundHostToken);
89
+ }
90
+ catch {
91
+ outbound = undefined;
92
+ }
93
+ if (outbound) {
94
+ await outbound.send({ ...target, content: text });
95
+ return;
96
+ }
97
+ await scoped(text);
98
+ };
99
+ }
100
+ function readOutboundSendTarget(input) {
101
+ const message = input;
102
+ const conversation = message?.conversation;
103
+ const endpointKeyRaw = conversation?.endpoint?.id;
104
+ const adapterRaw = conversation?.endpoint?.adapter;
105
+ const kind = conversation?.kind;
106
+ const id = conversation?.id;
107
+ if (endpointKeyRaw == null
108
+ || adapterRaw == null
109
+ || (kind !== 'private' && kind !== 'group' && kind !== 'channel')
110
+ || typeof id !== 'string'
111
+ || !id) {
112
+ return undefined;
113
+ }
114
+ const live = String(message?.metadata?.endpoint ?? message?.metadata?.endpointKey ?? '').trim();
115
+ return {
116
+ adapter: String(adapterRaw),
117
+ endpointKey: live || String(endpointKeyRaw),
118
+ conversation: {
119
+ kind,
120
+ id,
121
+ parent: conversation.parent,
122
+ threadId: typeof conversation.threadId === 'string' ? conversation.threadId : undefined,
123
+ },
124
+ };
125
+ }
75
126
  export function createEndpointRuntimeState() {
76
127
  return { endpoints: new Map() };
77
128
  }
@@ -94,8 +145,8 @@ function envSlug(text) {
94
145
  .toUpperCase();
95
146
  }
96
147
  /** 派生 endpoint 凭据的 env 键:`${ADAPTER}_${NAME}_${FIELD}`(如 `TELEGRAM_MY_BOT_TOKEN`) */
97
- export function buildEndpointEnvKey(adapterKey, endpointName, fieldKey) {
98
- return `${envSlug(adapterKey)}_${envSlug(endpointName)}_${envSlug(fieldKey)}`;
148
+ export function buildEndpointEnvKey(adapterKey, endpointId, fieldKey) {
149
+ return `${envSlug(adapterKey)}_${envSlug(endpointId)}_${envSlug(fieldKey)}`;
99
150
  }
100
151
  function escapeRegExp(text) {
101
152
  return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
@@ -162,13 +213,13 @@ export function listConfiguredEndpoints(adapterKey, projectRoot) {
162
213
  const endpoints = plugins[adapterKey]?.endpoints;
163
214
  if (!Array.isArray(endpoints))
164
215
  return [];
165
- return endpoints.filter((entry) => !!entry && typeof entry === 'object' && typeof entry.name === 'string');
216
+ return endpoints.filter((entry) => !!entry && typeof entry === 'object' && typeof entry.id === 'string');
166
217
  }
167
- function entryName(item) {
218
+ function entryId(item) {
168
219
  if (!isMap(item))
169
220
  return undefined;
170
- const name = item.get('name');
171
- return typeof name === 'string' && name ? name : undefined;
221
+ const id = item.get('id');
222
+ return typeof id === 'string' && id ? id : undefined;
172
223
  }
173
224
  /**
174
225
  * 确保 plugins.<adapterKey>.endpoints 存在并返回其 YAMLSeq(节点级操作,保留既有条目与注释)。
@@ -202,22 +253,22 @@ function ensureEndpointsSeq(doc, adapterKey) {
202
253
  }
203
254
  return doc.getIn(['plugins', adapterKey, 'endpoints']);
204
255
  }
205
- /** 追加 endpoint 到 plugins.<adapterKey>.endpoints;name 已存在时报错 */
256
+ /** 追加 endpoint 到 plugins.<adapterKey>.endpoints;id 已存在时报错 */
206
257
  export function addEndpointToConfig(adapterKey, entry, projectRoot) {
207
258
  const document = readConfigDocument(adapterKey, projectRoot);
208
259
  const seq = ensureEndpointsSeq(document.doc, adapterKey);
209
- if (seq.items.some((item) => entryName(item) === entry.name)) {
210
- throw new Error(`配置中已存在 ${adapterKey} endpoint「${entry.name}」,可先 ${adapterKey}.endpoint remove ${entry.name} 再重新添加`);
260
+ if (seq.items.some((item) => entryId(item) === entry.id)) {
261
+ throw new Error(`配置中已存在 ${adapterKey} endpoint「${entry.id}」,可先 ${adapterKey}.endpoint remove ${entry.id} 再重新添加`);
211
262
  }
212
263
  seq.items.push(document.doc.createNode(entry));
213
264
  writeConfigDocument(document);
214
265
  return document.filePath;
215
266
  }
216
- /** 按 name 移除 plugins.<adapterKey>.endpoints 项;不存在返回 false */
217
- export function removeEndpointFromConfig(adapterKey, name, projectRoot) {
267
+ /** 按 id 移除 plugins.<adapterKey>.endpoints 项;不存在返回 false */
268
+ export function removeEndpointFromConfig(adapterKey, id, projectRoot) {
218
269
  const document = readConfigDocument(adapterKey, projectRoot);
219
270
  const seq = ensureEndpointsSeq(document.doc, adapterKey);
220
- const next = seq.items.filter((item) => entryName(item) !== name);
271
+ const next = seq.items.filter((item) => entryId(item) !== id);
221
272
  if (next.length === seq.items.length) {
222
273
  return { removed: false, filePath: document.filePath };
223
274
  }
@@ -225,9 +276,9 @@ export function removeEndpointFromConfig(adapterKey, name, projectRoot) {
225
276
  writeConfigDocument(document);
226
277
  return { removed: true, filePath: document.filePath };
227
278
  }
228
- function endpointNameParam(params) {
229
- const name = params.name;
230
- return typeof name === 'string' && name.trim() ? name.trim() : undefined;
279
+ function endpointIdParam(params) {
280
+ const id = params.id;
281
+ return typeof id === 'string' && id.trim() ? id.trim() : undefined;
231
282
  }
232
283
  /** list 文案:运行中 + 配置中两段,footer 可选。 */
233
284
  export function formatEndpointList(spec, source) {
@@ -239,7 +290,7 @@ export function formatEndpointList(spec, source) {
239
290
  }
240
291
  else {
241
292
  for (const endpoint of running) {
242
- lines.push(endpoint.mode ? ` - ${endpoint.name}(${endpoint.mode})` : ` - ${endpoint.name}`);
293
+ lines.push(endpoint.mode ? ` - ${endpoint.id}(${endpoint.mode})` : ` - ${endpoint.id}`);
243
294
  }
244
295
  }
245
296
  lines.push(`【配置中的 ${spec.adapterDisplayName} endpoints】(zhin.config.yml → plugins.${spec.adapterKey}.endpoints)`);
@@ -249,7 +300,7 @@ export function formatEndpointList(spec, source) {
249
300
  else {
250
301
  for (const entry of source.configured) {
251
302
  const detail = spec.describeEntry?.(entry);
252
- lines.push(detail ? ` - ${entry.name}(${detail})` : ` - ${entry.name}`);
303
+ lines.push(detail ? ` - ${entry.id}(${detail})` : ` - ${entry.id}`);
253
304
  }
254
305
  }
255
306
  if (source.footer)
@@ -268,10 +319,10 @@ function addUsage(spec) {
268
319
  ].filter(Boolean).join(',');
269
320
  return marks ? `${field.key}(${marks})` : field.key;
270
321
  }).join('、')}`;
271
- return `用法:${spec.adapterKey}.endpoint add <name> <key=value...>${fieldText}`;
322
+ return `用法:${spec.adapterKey}.endpoint add <id> <key=value...>${fieldText}`;
272
323
  }
273
324
  /** add(kv 模式)的完整业务逻辑:解析 kv → 凭据写 .env → 追加 yaml;返回回复文本。 */
274
- export function addEndpointFromKeyValues(spec, name, args, projectRoot) {
325
+ export function addEndpointFromKeyValues(spec, id, args, projectRoot) {
275
326
  const fields = spec.fields ?? [];
276
327
  const known = new Map(fields.map((field) => [field.key, field]));
277
328
  const values = new Map();
@@ -293,14 +344,14 @@ export function addEndpointFromKeyValues(spec, name, args, projectRoot) {
293
344
  if (missing.length > 0) {
294
345
  return `缺少必填字段:${missing.map((field) => field.key).join('、')}。${addUsage(spec)}`;
295
346
  }
296
- const entry = { name };
347
+ const entry = { id };
297
348
  const envValues = {};
298
349
  for (const field of fields) {
299
350
  const value = values.get(field.key);
300
351
  if (value === undefined)
301
352
  continue;
302
353
  if (field.env) {
303
- const envKey = buildEndpointEnvKey(spec.adapterKey, name, field.key);
354
+ const envKey = buildEndpointEnvKey(spec.adapterKey, id, field.key);
304
355
  envValues[envKey] = value;
305
356
  entry[field.key] = `\${${envKey}}`;
306
357
  }
@@ -309,11 +360,10 @@ export function addEndpointFromKeyValues(spec, name, args, projectRoot) {
309
360
  }
310
361
  }
311
362
  try {
312
- // 先写配置(重名等校验失败时不留孤儿 .env 键),再落 .env 凭据
313
363
  const filePath = addEndpointToConfig(spec.adapterKey, entry, projectRoot);
314
364
  if (Object.keys(envValues).length > 0)
315
365
  persistEndpointEnvValues(envValues, projectRoot);
316
- return (`✅ endpoint「${name}」已追加到 ${filePath} 的 plugins.${spec.adapterKey}.endpoints` +
366
+ return (`✅ endpoint「${id}」已追加到 ${filePath} 的 plugins.${spec.adapterKey}.endpoints` +
317
367
  `${Object.keys(envValues).length > 0 ? '(凭据已写入 .env)' : ''}。\n` +
318
368
  '⚠️ 需重启 zhin 后新 endpoint 才会生效。');
319
369
  }
@@ -322,10 +372,10 @@ export function addEndpointFromKeyValues(spec, name, args, projectRoot) {
322
372
  }
323
373
  }
324
374
  /** remove 的完整业务逻辑:从 yaml 移除;返回回复文本。 */
325
- export function removeEndpointByName(spec, name, projectRoot) {
326
- const trimmed = name.trim();
375
+ export function removeEndpointById(spec, id, projectRoot) {
376
+ const trimmed = id.trim();
327
377
  if (!trimmed)
328
- return `用法:${spec.adapterKey}.endpoint remove <name>`;
378
+ return `用法:${spec.adapterKey}.endpoint remove <id>`;
329
379
  try {
330
380
  const { removed, filePath } = removeEndpointFromConfig(spec.adapterKey, trimmed, projectRoot);
331
381
  if (!removed) {
@@ -355,32 +405,32 @@ export function createEndpointCommands(spec, defineCommand) {
355
405
  add: defineCommand({
356
406
  description: spec.addDescription
357
407
  ?? `手动添加 ${spec.adapterDisplayName} endpoint(凭据写入 .env 并追加到 zhin.config.yml,重启生效)`,
358
- params: { name: { type: 'string', description: 'endpoint 名称' } },
408
+ params: { id: { type: 'string', description: 'endpoint ID' } },
359
409
  execute({ config, input, params, args, use }) {
360
410
  if (!isEndpointOperator(config, input))
361
411
  return forbidden;
362
- const name = endpointNameParam(params);
412
+ const id = endpointIdParam(params);
363
413
  if (spec.bindFlow) {
364
414
  return spec.bindFlow({
365
- name,
366
- reply: extractEndpointCommandReply(input),
415
+ id,
416
+ reply: createDurableEndpointCommandReply(input, use),
367
417
  config,
368
418
  input,
369
419
  use,
370
420
  });
371
421
  }
372
- if (!name)
422
+ if (!id)
373
423
  return addUsage(spec);
374
- return addEndpointFromKeyValues(spec, name, args);
424
+ return addEndpointFromKeyValues(spec, id, args);
375
425
  },
376
426
  }),
377
427
  remove: defineCommand({
378
428
  description: `从 zhin.config.yml 的 plugins.${spec.adapterKey}.endpoints 移除指定 endpoint(重启生效)`,
379
- params: { name: { type: 'string', description: 'endpoint 名称' } },
429
+ params: { id: { type: 'string', description: 'endpoint ID' } },
380
430
  execute({ config, input, params }) {
381
431
  if (!isEndpointOperator(config, input))
382
432
  return forbidden;
383
- return removeEndpointByName(spec, String(params.name ?? ''));
433
+ return removeEndpointById(spec, String(params.id ?? ''));
384
434
  },
385
435
  }),
386
436
  });
@@ -1,4 +1,4 @@
1
- import { type ConversationTarget, type MessageTarget } from '@zhin.js/im-contract';
1
+ import type { ConversationRef, MessageRef } from '@zhin.js/im-contract';
2
2
  /**
3
3
  * Transport-neutral control plane for a live endpoint.
4
4
  *
@@ -7,24 +7,20 @@ import { type ConversationTarget, type MessageTarget } from '@zhin.js/im-contrac
7
7
  * to know a protocol's method names or identifier layout.
8
8
  */
9
9
  export interface EndpointControl {
10
- recall?(message: MessageTarget): Promise<void>;
11
- edit?(message: MessageTarget, content: unknown): Promise<string | null>;
12
- addReaction?(message: MessageTarget, emoji: string, hint?: {
10
+ recall?(message: MessageRef): Promise<void>;
11
+ edit?(message: MessageRef, content: unknown): Promise<string | null>;
12
+ addReaction?(message: MessageRef, emoji: string, hint?: {
13
13
  readonly sceneType?: string;
14
14
  readonly channelId?: string;
15
15
  }): Promise<string | null>;
16
- removeReaction?(message: MessageTarget, reactionId: string): Promise<void>;
17
- typing?(conversation: ConversationTarget, active?: boolean): Promise<void>;
16
+ removeReaction?(message: MessageRef, reactionId: string): Promise<void>;
17
+ typing?(conversation: ConversationRef, active?: boolean): Promise<void>;
18
18
  }
19
19
  export interface EndpointWithControl {
20
20
  readonly control?: EndpointControl;
21
21
  }
22
- /**
23
- * Resolves the public control port. The legacy branch is deliberately kept in
24
- * Adapter only: it is a migration bridge for existing protocol endpoints, not
25
- * an IM Core extension point. New adapters must expose `control` directly.
26
- */
27
- export declare function resolveEndpointControl(endpoint: unknown): EndpointControl | undefined;
22
+ /** Reads the canonical control port without probing protocol-specific methods. */
23
+ export declare function endpointControlOf(endpoint: unknown): EndpointControl | undefined;
28
24
  /** Checks only an Endpoint's explicit `control` port, never the legacy bridge. */
29
25
  export declare function hasExplicitEndpointOperation(endpoint: unknown, operation: 'recall' | 'edit' | 'reaction' | 'typing'): boolean;
30
26
  /** Rejects a declaration that cannot be fulfilled by the explicit control port. */
@@ -1,54 +1,9 @@
1
- import { formatLegacyConversationRef, formatLegacyMessageRef, } from '@zhin.js/im-contract';
2
- /**
3
- * Resolves the public control port. The legacy branch is deliberately kept in
4
- * Adapter only: it is a migration bridge for existing protocol endpoints, not
5
- * an IM Core extension point. New adapters must expose `control` directly.
6
- */
7
- export function resolveEndpointControl(endpoint) {
1
+ /** Reads the canonical control port without probing protocol-specific methods. */
2
+ export function endpointControlOf(endpoint) {
8
3
  if (!endpoint || typeof endpoint !== 'object')
9
4
  return undefined;
10
5
  const explicit = endpoint.control;
11
- if (explicit && typeof explicit === 'object')
12
- return explicit;
13
- const legacy = endpoint;
14
- const recall = legacy.recallMessage ?? legacy.$recallMessage;
15
- const edit = legacy.editMessage ?? legacy.$editMessage;
16
- const addReaction = legacy.addReaction ?? legacy.$addReaction;
17
- const removeReaction = legacy.removeReaction ?? legacy.$removeReaction;
18
- const typing = legacy.typing ?? legacy.$typing;
19
- if (!recall && !edit && !addReaction && !removeReaction && !typing)
20
- return undefined;
21
- return Object.freeze({
22
- ...(recall
23
- ? { recall: (message) => recall.call(endpoint, legacyMessageId(message)) }
24
- : {}),
25
- ...(edit
26
- ? {
27
- edit: (message, content) => edit.call(endpoint, legacyMessageId(message), content),
28
- }
29
- : {}),
30
- ...(addReaction
31
- ? {
32
- addReaction: (message, emoji, hint) => addReaction.call(endpoint, legacyMessageId(message), emoji, hint),
33
- }
34
- : {}),
35
- ...(removeReaction
36
- ? {
37
- removeReaction: (message, reactionId) => removeReaction.call(endpoint, legacyMessageId(message), reactionId),
38
- }
39
- : {}),
40
- ...(typing
41
- ? {
42
- typing: (conversation, active) => typing.call(endpoint, legacyConversationTarget(conversation), active),
43
- }
44
- : {}),
45
- });
46
- }
47
- function legacyMessageId(message) {
48
- return typeof message === 'string' ? message : formatLegacyMessageRef(message);
49
- }
50
- function legacyConversationTarget(conversation) {
51
- return typeof conversation === 'string' ? conversation : formatLegacyConversationRef(conversation);
6
+ return explicit && typeof explicit === 'object' ? explicit : undefined;
52
7
  }
53
8
  /** Checks only an Endpoint's explicit `control` port, never the legacy bridge. */
54
9
  export function hasExplicitEndpointOperation(endpoint, operation) {
@@ -19,7 +19,7 @@
19
19
  *
20
20
  * 迁移指引(以 napcat/milky/onebot WS endpoint 为例):
21
21
  * 1. 删除 #started / #stopping / #reconnectTimer / #heartbeatTimer / opened 旗标,
22
- * 构造器里 `this.#lifecycle = createEndpointLifecycle({ name: config.name, reconnect, heartbeat })`。
22
+ * 构造器里 `this.#lifecycle = createEndpointLifecycle({ name: config.id, reconnect, heartbeat })`。
23
23
  * 2. `start()` 改为:
24
24
  * ```ts
25
25
  * this.#unregisterAgent = registerXxxAgentEndpoint(name, this); // agent 注册仍在适配器侧
package/lib/provider.js CHANGED
@@ -16,29 +16,17 @@ const adapterFeature = defineFeatureProvider({
16
16
  },
17
17
  runtime: {
18
18
  async project(slots, context) {
19
- const index = await AdapterIndex.create(slots, context.snapshot);
20
- let previousIndex;
19
+ const index = await AdapterIndex.create(slots, context.snapshot, context.signal);
21
20
  return {
22
21
  value: index,
23
22
  dispose: () => index.stop(),
24
23
  handoff: {
25
- quiescePrevious(previous) {
26
- previousIndex = previousAdapterIndex(previous);
27
- return previousIndex?.close();
28
- },
29
- activateNext: () => index.start(),
24
+ activateNext: (signal) => index.activate(signal),
30
25
  deactivateNext: () => index.stop(),
31
- resumePrevious() {
32
- previousIndex?.open();
33
- },
34
- openNext: () => index.open(),
35
26
  },
36
27
  };
37
28
  },
38
29
  },
39
30
  });
40
- function previousAdapterIndex(snapshot) {
41
- return snapshot.projections.get(adapterFeatureId);
42
- }
43
31
  export { adapterFeature };
44
32
  export default adapterFeature;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhin.js/adapter",
3
- "version": "1.1.5",
3
+ "version": "1.1.8",
4
4
  "description": "Convention-based Adapter and Endpoint Feature for Zhin Plugin Runtime",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -18,15 +18,15 @@
18
18
  ],
19
19
  "dependencies": {
20
20
  "yaml": "^2.9.0",
21
- "@zhin.js/feature-kit": "1.0.6",
22
- "@zhin.js/im-contract": "1.0.1",
23
- "@zhin.js/logger": "1.0.75",
24
- "@zhin.js/plugin-runtime": "1.1.3"
21
+ "@zhin.js/feature-kit": "1.0.9",
22
+ "@zhin.js/im-contract": "1.0.3",
23
+ "@zhin.js/logger": "1.0.76",
24
+ "@zhin.js/plugin-runtime": "1.1.6"
25
25
  },
26
26
  "devDependencies": {
27
27
  "@types/node": "^26.1.2",
28
28
  "typescript": "^6.0.3",
29
- "@zhin.js/command": "1.0.7"
29
+ "@zhin.js/command": "1.0.12"
30
30
  },
31
31
  "zhin": {
32
32
  "protocol": 1,