@zhin.js/adapter-napcat 1.1.18 → 1.1.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.
Files changed (57) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/README.md +8 -5
  3. package/adapters/napcat/index.js +2 -29
  4. package/adapters/napcat/index.ts +3 -35
  5. package/agents/napcat/skills/napcat-account/tools/set_profile/index.js +1 -1
  6. package/agents/napcat/skills/napcat-account/tools/set_profile/index.ts +1 -1
  7. package/agents/napcat/skills/napcat-group-files-history/tools/upload_group_file/index.js +1 -1
  8. package/agents/napcat/skills/napcat-group-files-history/tools/upload_group_file/index.ts +1 -1
  9. package/agents/napcat/skills/napcat-group-settings/tools/set_title/index.js +1 -1
  10. package/agents/napcat/skills/napcat-group-settings/tools/set_title/index.ts +1 -1
  11. package/agents/napcat/skills/napcat-media/tools/get_mini_app_ark/index.js +1 -1
  12. package/agents/napcat/skills/napcat-media/tools/get_mini_app_ark/index.ts +1 -1
  13. package/commands/napcat/endpoint/definition.js +0 -2
  14. package/commands/napcat/endpoint/definition.ts +0 -2
  15. package/lib/adapter.d.ts +23 -0
  16. package/lib/adapter.js +42 -0
  17. package/lib/client.d.ts +53 -16
  18. package/lib/client.js +53 -10
  19. package/lib/console.d.ts +1 -1
  20. package/lib/console.js +2 -5
  21. package/lib/endpoint-management.d.ts +2 -2
  22. package/lib/endpoint-management.js +13 -13
  23. package/lib/http-endpoint.d.ts +5 -15
  24. package/lib/http-endpoint.js +44 -36
  25. package/lib/identity.d.ts +3 -0
  26. package/lib/identity.js +32 -0
  27. package/lib/index.d.ts +3 -2
  28. package/lib/index.js +2 -2
  29. package/lib/onebot-get-msg.d.ts +2 -2
  30. package/lib/onebot-get-msg.js +2 -2
  31. package/lib/protocol.d.ts +5 -0
  32. package/lib/protocol.js +23 -11
  33. package/lib/ws-endpoint.d.ts +6 -16
  34. package/lib/ws-endpoint.js +57 -48
  35. package/lib/ws-transport.d.ts +18 -12
  36. package/lib/ws-transport.js +28 -72
  37. package/lib/ws-types.d.ts +0 -5
  38. package/lib/wss-endpoint.d.ts +7 -15
  39. package/lib/wss-endpoint.js +56 -47
  40. package/package.json +18 -18
  41. package/plugin.js +0 -3
  42. package/src/adapter.ts +45 -0
  43. package/src/client.ts +91 -19
  44. package/src/console.ts +2 -5
  45. package/src/endpoint-management.ts +19 -15
  46. package/src/http-endpoint.ts +47 -45
  47. package/src/identity.ts +29 -0
  48. package/src/index.ts +9 -5
  49. package/src/onebot-get-msg.ts +4 -4
  50. package/src/protocol.ts +26 -14
  51. package/src/ws-endpoint.ts +61 -54
  52. package/src/ws-transport.ts +40 -101
  53. package/src/ws-types.ts +0 -6
  54. package/src/wss-endpoint.ts +60 -54
  55. package/lib/napcat-runtime-state.d.ts +0 -1
  56. package/lib/napcat-runtime-state.js +0 -6
  57. package/src/napcat-runtime-state.ts +0 -7
@@ -3,7 +3,7 @@ import { EndpointDeliveryError } from '@zhin.js/im-contract';
3
3
  * NapCat reverse WSS endpoint — accepts inbound WebSocket from NapCat.
4
4
  */
5
5
  import {
6
- Endpoint,
6
+ createEndpointSpecification, endpointSpec, type Endpoint, type EndpointSpecification,
7
7
  createEndpointLifecycle,
8
8
  createRecallEndpointControl,
9
9
  type EndpointLifecycle,
@@ -38,46 +38,51 @@ import { receiveNapCatSideEvent } from './side-event-dispatch.js';
38
38
  import { createNapCatContentPort } from './onebot-get-msg.js';
39
39
  import { NapcatClient } from './client.js';
40
40
  import {
41
- callNapCatWsAction,
42
- handleNapCatWsMessage,
43
- rejectAllPending,
44
- } from './ws-transport.js';
45
- import { NapCatWsEndpoint } from './ws-endpoint.js';
46
- import {
47
- type NapCatPendingAction,
48
41
  type NapCatWsSocket,
49
42
  } from './ws-types.js';
50
43
  import { verifyNapCatAccessToken } from './wss-auth.js';
51
44
 
52
45
  export interface NapCatWssEndpointOptions {
53
46
  readonly id: CapabilityId;
47
+ readonly signal?: AbortSignal;
54
48
  readonly http: HttpHost;
55
49
  readonly config: NapCatWssConfig;
56
50
  }
57
51
 
58
- export class NapCatWssEndpoint extends Endpoint<NapcatClient> {
59
- readonly client = new NapcatClient((action, params) => this.#callApi(action, params));
52
+ export class NapCatWssEndpoint extends NapcatClient implements Endpoint {
53
+ readonly [endpointSpec]: EndpointSpecification;
60
54
  readonly #logger!: ReturnType<typeof getAdapterLogger>;
61
55
 
62
56
  readonly #options: NapCatWssEndpointOptions;
63
57
  readonly #inboundDeduper = new InboundMessageDeduper();
64
- readonly management: EndpointManagement = createNapCatEndpointManagement(this.client);
65
- readonly control: EndpointControl = createRecallEndpointControl((id) => this.recallMessage(id));
66
- readonly content = createNapCatContentPort((action, params) => this.client.callApi(action, params));
58
+ readonly #management: EndpointManagement = createNapCatEndpointManagement(this);
59
+ readonly #control: EndpointControl = createRecallEndpointControl((id, signal) => this.#recallMessage(id, signal));
60
+ readonly #content = createNapCatContentPort(request => this.requestApi(request));
67
61
  readonly #connectionLifecycle: EndpointLifecycle;
68
- #ws?: NapCatWsSocket;
69
62
  #wsRelease?: () => void;
70
63
  #connectionTask = Promise.resolve();
71
- #requestId = { value: 0 };
72
- #pending = new Map<string, NapCatPendingAction>();
73
64
  #open = false;
74
65
  #started = false;
75
66
  #stopped = false;
76
67
 
77
68
  constructor(options: NapCatWssEndpointOptions) {
78
- super();
69
+ super({ config: options.config, signal: options.signal });
79
70
  this.#logger = getAdapterLogger('napcat', options.config.id);
80
71
  this.#options = options;
72
+ const readTransportState = () => this.#transportState;
73
+ this[endpointSpec] = createEndpointSpecification(this, {
74
+ name: options.config.id,
75
+ start: () => this.#start(), open: () => this.#openEndpoint(),
76
+ close: () => this.#close(), stop: () => this.#stop(),
77
+ send: request => this.#send(request),
78
+ management: this.#management, control: this.#control, content: this.#content,
79
+ get transportState() { return readTransportState(); },
80
+ });
81
+ this.on('event', event => {
82
+ void this[endpointSpec].events.run(() => this.#admit(event)).catch((error: unknown) => {
83
+ this.#logger.debug(formatCompact({ op: 'napcat_receive_failed', error: error instanceof Error ? error.message : String(error) }));
84
+ });
85
+ });
81
86
  this.#connectionLifecycle = createEndpointLifecycle({
82
87
  name: `${options.config.id}:inbound`,
83
88
  reconnect: false,
@@ -85,15 +90,19 @@ export class NapCatWssEndpoint extends Endpoint<NapcatClient> {
85
90
  });
86
91
  }
87
92
 
88
- get transportState(): EndpointTransportState {
93
+ get #transportState(): EndpointTransportState {
89
94
  if (this.#stopped) return 'stopped';
90
95
  if (!this.#started) return 'idle';
91
96
  if (this.#connectionLifecycle.state === 'closed') return 'closed';
92
- if (this.#ws?.readyState === 1) return 'open';
97
+ if (this.transportSocket?.readyState === 1) return 'open';
93
98
  return this.#connectionLifecycle.state === 'connecting' ? 'connecting' : 'idle';
94
99
  }
95
100
 
96
- async start(): Promise<void> {
101
+ protected override get actionSocket(): NapCatWsSocket | undefined {
102
+ return this.#connectionLifecycle.state === 'closed' ? undefined : this.transportSocket;
103
+ }
104
+
105
+ async #start(): Promise<void> {
97
106
  if (this.#started) return;
98
107
  this.#stopped = false;
99
108
  this.#started = true;
@@ -117,31 +126,31 @@ export class NapCatWssEndpoint extends Endpoint<NapcatClient> {
117
126
  }));
118
127
  }
119
128
 
120
- open(): void {
129
+ #openEndpoint(): void {
121
130
  this.#open = true;
122
131
  }
123
132
 
124
- close(): void {
133
+ #close(): void {
125
134
  this.#open = false;
126
135
  }
127
136
 
128
- async stop(): Promise<void> {
137
+ async #stop(): Promise<void> {
129
138
  this.#stopped = true;
130
139
  this.#open = false;
131
140
  this.#wsRelease?.();
132
141
  this.#wsRelease = undefined;
133
142
  await this.#connectionTask;
134
143
  await this.#connectionLifecycle.stop();
135
- rejectAllPending(this.#pending);
144
+ this.rpc.disconnect();
136
145
  this.#inboundDeduper.clear();
137
- this.#ws = undefined;
146
+ this.transportSocket = undefined;
138
147
  this.#started = false;
139
148
  }
140
149
 
141
- async send({ conversation, payload }: EndpointSendRequest): Promise<string> {
150
+ async #send({ conversation, payload, signal }: EndpointSendRequest): Promise<string> {
142
151
  const message = formatOutboundSegments(payload);
143
152
  const { action, params } = buildSendAction(napcatOutboundTarget(conversation), message);
144
- const data = await this.client.callApi(action, params) as { message_id?: number | string } | undefined;
153
+ const data = await this.requestApi({ action, params, signal }) as { message_id?: number | string } | undefined;
145
154
  const rawId = data?.message_id;
146
155
  if ((typeof rawId !== 'number' && typeof rawId !== 'string') || String(rawId).trim() === '' || (typeof rawId === 'number' && !Number.isFinite(rawId))) {
147
156
  throw new EndpointDeliveryError('delivery_unconfirmed', 'Message delivery is unconfirmed: platform returned no message_id', 'unknown');
@@ -149,18 +158,17 @@ export class NapCatWssEndpoint extends Endpoint<NapcatClient> {
149
158
  return String(rawId);
150
159
  }
151
160
 
152
- async recallMessage(messageId: string): Promise<void> {
161
+ async #recallMessage(messageId: string, signal?: AbortSignal): Promise<void> {
153
162
  if (!messageId) return;
154
- await this.client.callApi('delete_msg', { message_id: Number(messageId) });
163
+ await this.requestApi({ action: 'delete_msg', params: { message_id: Number(messageId) }, signal });
155
164
  }
156
165
 
157
- #callApi(action: string, params: Record<string, unknown> = {}): Promise<unknown> {
158
- return callNapCatWsAction(this.#connectionLifecycle.state === 'closed' ? undefined : this.#ws, this.#pending, this.#requestId, action, params);
159
- }
160
166
 
161
- admit(ev: NapCatEvent): void {
162
- if (!this.#open) return;
163
- void this.emitPlatform(napCatPlatformEventName(ev), ev).catch((error) => {
167
+
168
+ #admit(ev: NapCatEvent): void {
169
+ const nativeEvent = ev;
170
+ if (!this.#open && !this.#started) return;
171
+ void this[endpointSpec].events.platform(napCatPlatformEventName(ev), ev).catch((error) => {
164
172
  this.#logger.warn(formatCompact({
165
173
  op: 'napcat_platform_event_failed',
166
174
  error: error instanceof Error ? error.message : String(error),
@@ -168,9 +176,9 @@ export class NapCatWssEndpoint extends Endpoint<NapcatClient> {
168
176
  });
169
177
  if (!isMessageEvent(ev)) {
170
178
  receiveNapCatSideEvent(
171
- (name, payload) => this.emit(name, payload),
179
+ (name, payload) => this[endpointSpec].events.emit(name, payload),
172
180
  this.#options.config.id,
173
- this.client,
181
+ this,
174
182
  ev,
175
183
  this.#logger,
176
184
  );
@@ -182,10 +190,12 @@ export class NapCatWssEndpoint extends Endpoint<NapcatClient> {
182
190
  if (Array.isArray(ev.message) || typeof ev.message === 'string') {
183
191
  ev = { ...ev, message: normalizeMessage(ev.message) };
184
192
  }
185
- const conversation = napcatInboundConversation(String(this.#options.id), ev);
193
+ const conversation = { ...napcatInboundConversation(String(this.#options.id), ev), endpoint: this[endpointSpec].identity };
186
194
  const nickname = senderNickname(ev);
187
195
  const mentioned = isNapCatBotMentioned(ev);
188
- void this.emit('message.receive', {
196
+ void this[endpointSpec].events.message({
197
+ event: nativeEvent,
198
+ ...(typeof nativeEvent.time === 'number' ? { platformSentAt: nativeEvent.time * 1000 } : {}),
189
199
  conversation,
190
200
  message: { conversation, id: msgId },
191
201
  content: formatInboundContent(ev),
@@ -225,12 +235,12 @@ export class NapCatWssEndpoint extends Endpoint<NapcatClient> {
225
235
  }
226
236
  const socket = connection.socket as unknown as NapCatWsSocket;
227
237
  await this.#connectionLifecycle.stop();
228
- rejectAllPending(this.#pending, 'Connection replaced');
238
+ this.rpc.disconnect('Connection replaced');
229
239
  await this.#connectionLifecycle.start(async (lifecycleHandle) => {
230
- this.#ws = socket;
240
+ this.transportSocket = socket;
231
241
  lifecycleHandle.onForceClose(() => {
232
- if (this.#ws === socket) {
233
- rejectAllPending(this.#pending);
242
+ if (this.transportSocket === socket) {
243
+ this.rpc.disconnect();
234
244
  }
235
245
  try {
236
246
  socket.close();
@@ -246,25 +256,21 @@ export class NapCatWssEndpoint extends Endpoint<NapcatClient> {
246
256
  }
247
257
  }, this.#options.config.heartbeat_interval);
248
258
  socket.on('pong', () => {
249
- if (this.#ws === socket) this.#connectionLifecycle.notifyHeartbeatAck();
259
+ if (this.transportSocket === socket) this.#connectionLifecycle.notifyHeartbeatAck();
250
260
  });
251
261
  socket.on('message', (data) => {
252
- if (this.#ws !== socket || this.#connectionLifecycle.state === 'closed') return;
262
+ if (this.transportSocket !== socket || this.#connectionLifecycle.state === 'closed') return;
253
263
  this.#connectionLifecycle.notifyHeartbeatAck();
254
- handleNapCatWsMessage(data, {
255
- endpointId: this.#options.config.id,
256
- pending: this.#pending,
257
- admit: (event) => this.admit(event),
258
- });
264
+ this.rpc.receive(data, event => this.ingest(event));
259
265
  });
260
266
  socket.on('error', (error) => {
261
- if (this.#ws !== socket) return;
267
+ if (this.transportSocket !== socket) return;
262
268
  this.#logger.warn(formatCompact({ op: 'ws_error', error: String(error) }));
263
269
  });
264
270
  socket.on('close', () => {
265
- if (this.#ws !== socket) return;
266
- this.#ws = undefined;
267
- rejectAllPending(this.#pending);
271
+ if (this.transportSocket !== socket) return;
272
+ this.transportSocket = undefined;
273
+ this.rpc.disconnect();
268
274
  lifecycleHandle.notifyClosed(new Error('NapCat reverse WebSocket closed'));
269
275
  });
270
276
  });
@@ -1 +0,0 @@
1
- export declare const napcatRuntimeStateToken: import("zhin.js").Token<import("@zhin.js/adapter").EndpointRuntimeState>;
@@ -1,6 +0,0 @@
1
- /**
2
- * NapCat 插件实例的运行时状态:adapter create() 注册的 endpoint 列表。
3
- * 由 plugin.ts setup() provide,adapter create 与 `napcat endpoint` 命令共享(同一 owner generation)。
4
- */
5
- import { defineEndpointRuntimeStateToken } from 'zhin.js/adapter';
6
- export const napcatRuntimeStateToken = defineEndpointRuntimeStateToken('napcat');
@@ -1,7 +0,0 @@
1
- /**
2
- * NapCat 插件实例的运行时状态:adapter create() 注册的 endpoint 列表。
3
- * 由 plugin.ts setup() provide,adapter create 与 `napcat endpoint` 命令共享(同一 owner generation)。
4
- */
5
- import { defineEndpointRuntimeStateToken } from 'zhin.js/adapter';
6
-
7
- export const napcatRuntimeStateToken = defineEndpointRuntimeStateToken('napcat');