@basmilius/apple-companion-link 0.1.3 → 0.2.0
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.d.ts +6 -7
- package/dist/index.js +163 -147
- package/dist/pairing.d.ts +11 -2
- package/dist/protocol.d.ts +10 -9
- package/dist/stream.d.ts +7 -0
- package/package.json +3 -3
- package/dist/socket.d.ts +0 -8
- package/dist/verify.d.ts +0 -7
- /package/dist/{messages.d.ts → frame.d.ts} +0 -0
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
3
|
-
export
|
|
4
|
-
export
|
|
5
|
-
export
|
|
6
|
-
export
|
|
7
|
-
export { default as CompanionLink } from "./protocol";
|
|
1
|
+
export { default as Protocol } from "./protocol";
|
|
2
|
+
export { default as Stream } from "./stream";
|
|
3
|
+
export { Pairing, Verify } from "./pairing";
|
|
4
|
+
export * from "./const";
|
|
5
|
+
export * from "./utils";
|
|
6
|
+
export type * from "./types";
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
// src/protocol.ts
|
|
2
|
+
import { randomInt as randomInt2 } from "node:crypto";
|
|
3
|
+
import { COMPANION_LINK_SERVICE, Context, waitFor } from "@basmilius/apple-common";
|
|
4
|
+
import { OPack as OPack2, Plist } from "@basmilius/apple-encoding";
|
|
5
|
+
|
|
1
6
|
// src/const.ts
|
|
2
7
|
var HidCommand = {
|
|
3
8
|
Up: 1,
|
|
@@ -35,27 +40,8 @@ var MediaControlCommand = {
|
|
|
35
40
|
GetCaptionSettings: 12,
|
|
36
41
|
SetCaptionSettings: 13
|
|
37
42
|
};
|
|
38
|
-
// src/utils.ts
|
|
39
|
-
function convertAttentionState(state) {
|
|
40
|
-
switch (state) {
|
|
41
|
-
case 1:
|
|
42
|
-
return "asleep";
|
|
43
|
-
case 2:
|
|
44
|
-
return "screensaver";
|
|
45
|
-
case 3:
|
|
46
|
-
return "awake";
|
|
47
|
-
case 4:
|
|
48
|
-
return "idle";
|
|
49
|
-
default:
|
|
50
|
-
return "unknown";
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
// src/protocol.ts
|
|
54
|
-
import { randomInt as randomInt2 } from "node:crypto";
|
|
55
|
-
import { reporter as reporter2, waitFor } from "@basmilius/apple-common";
|
|
56
|
-
import { OPack as OPack2, Plist } from "@basmilius/apple-encoding";
|
|
57
43
|
|
|
58
|
-
// src/
|
|
44
|
+
// src/frame.ts
|
|
59
45
|
var FrameType = {
|
|
60
46
|
Unknown: 0,
|
|
61
47
|
Noop: 1,
|
|
@@ -97,16 +83,19 @@ var PairFrameTypes = [
|
|
|
97
83
|
];
|
|
98
84
|
|
|
99
85
|
// src/pairing.ts
|
|
100
|
-
import { AccessoryPair } from "@basmilius/apple-common";
|
|
101
|
-
class
|
|
86
|
+
import { AccessoryPair, AccessoryVerify, hkdf } from "@basmilius/apple-common";
|
|
87
|
+
class Pairing {
|
|
102
88
|
get internal() {
|
|
103
89
|
return this.#internal;
|
|
104
90
|
}
|
|
91
|
+
get stream() {
|
|
92
|
+
return this.#stream;
|
|
93
|
+
}
|
|
105
94
|
#internal;
|
|
106
|
-
#
|
|
95
|
+
#stream;
|
|
107
96
|
constructor(protocol) {
|
|
108
|
-
this.#internal = new AccessoryPair(this.#request.bind(this));
|
|
109
|
-
this.#
|
|
97
|
+
this.#internal = new AccessoryPair(protocol.context, this.#request.bind(this));
|
|
98
|
+
this.#stream = protocol.stream;
|
|
110
99
|
}
|
|
111
100
|
async start() {
|
|
112
101
|
await this.#internal.start();
|
|
@@ -119,7 +108,7 @@ class CompanionLinkPairing {
|
|
|
119
108
|
}
|
|
120
109
|
async#request(step, data) {
|
|
121
110
|
const frameType = step === "m1" ? FrameType.PS_Start : FrameType.PS_Next;
|
|
122
|
-
const [, response] = await this.#
|
|
111
|
+
const [, response] = await this.#stream.exchange(frameType, {
|
|
123
112
|
_pd: data,
|
|
124
113
|
_pwTy: 1
|
|
125
114
|
});
|
|
@@ -130,24 +119,88 @@ class CompanionLinkPairing {
|
|
|
130
119
|
}
|
|
131
120
|
}
|
|
132
121
|
|
|
133
|
-
|
|
122
|
+
class Verify {
|
|
123
|
+
get internal() {
|
|
124
|
+
return this.#internal;
|
|
125
|
+
}
|
|
126
|
+
get stream() {
|
|
127
|
+
return this.#stream;
|
|
128
|
+
}
|
|
129
|
+
#internal;
|
|
130
|
+
#stream;
|
|
131
|
+
constructor(protocol) {
|
|
132
|
+
this.#internal = new AccessoryVerify(protocol.context, this.#request.bind(this));
|
|
133
|
+
this.#stream = protocol.stream;
|
|
134
|
+
}
|
|
135
|
+
async start(credentials) {
|
|
136
|
+
const keys = await this.#internal.start(credentials);
|
|
137
|
+
const accessoryToControllerKey = hkdf({
|
|
138
|
+
hash: "sha512",
|
|
139
|
+
key: keys.sharedSecret,
|
|
140
|
+
length: 32,
|
|
141
|
+
salt: Buffer.alloc(0),
|
|
142
|
+
info: Buffer.from("ServerEncrypt-main")
|
|
143
|
+
});
|
|
144
|
+
const controllerToAccessoryKey = hkdf({
|
|
145
|
+
hash: "sha512",
|
|
146
|
+
key: keys.sharedSecret,
|
|
147
|
+
length: 32,
|
|
148
|
+
salt: Buffer.alloc(0),
|
|
149
|
+
info: Buffer.from("ClientEncrypt-main")
|
|
150
|
+
});
|
|
151
|
+
return {
|
|
152
|
+
accessoryToControllerKey,
|
|
153
|
+
controllerToAccessoryKey,
|
|
154
|
+
pairingId: keys.pairingId,
|
|
155
|
+
sharedSecret: keys.sharedSecret
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
async#request(step, data) {
|
|
159
|
+
const frameType = step === "m1" ? FrameType.PV_Start : FrameType.PV_Next;
|
|
160
|
+
const [, response] = await this.#stream.exchange(frameType, {
|
|
161
|
+
_pd: data,
|
|
162
|
+
_auTy: 4
|
|
163
|
+
});
|
|
164
|
+
if (typeof response !== "object" || response === null) {
|
|
165
|
+
throw new Error("Invalid response from receiver.");
|
|
166
|
+
}
|
|
167
|
+
return response["_pd"];
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// src/utils.ts
|
|
172
|
+
function convertAttentionState(state) {
|
|
173
|
+
switch (state) {
|
|
174
|
+
case 1:
|
|
175
|
+
return "asleep";
|
|
176
|
+
case 2:
|
|
177
|
+
return "screensaver";
|
|
178
|
+
case 3:
|
|
179
|
+
return "awake";
|
|
180
|
+
case 4:
|
|
181
|
+
return "idle";
|
|
182
|
+
default:
|
|
183
|
+
return "unknown";
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// src/stream.ts
|
|
134
188
|
import { randomInt } from "node:crypto";
|
|
135
|
-
import { Chacha20, ENCRYPTION, EncryptionAwareConnection
|
|
189
|
+
import { Chacha20, ENCRYPTION, EncryptionAwareConnection } from "@basmilius/apple-common";
|
|
136
190
|
import { OPack } from "@basmilius/apple-encoding";
|
|
137
|
-
var
|
|
191
|
+
var HEADER_SIZE = 4;
|
|
138
192
|
|
|
139
|
-
class
|
|
140
|
-
get #
|
|
193
|
+
class Stream extends EncryptionAwareConnection {
|
|
194
|
+
get #encryptionState() {
|
|
141
195
|
return this[ENCRYPTION];
|
|
142
196
|
}
|
|
143
197
|
#queue = {};
|
|
144
198
|
#buffer = Buffer.alloc(0);
|
|
145
199
|
#xid;
|
|
146
|
-
constructor(address, port) {
|
|
147
|
-
super(address, port);
|
|
200
|
+
constructor(context, address, port) {
|
|
201
|
+
super(context, address, port);
|
|
148
202
|
this.#xid = randomInt(0, 2 ** 16);
|
|
149
|
-
this.
|
|
150
|
-
this.on("data", this.onData);
|
|
203
|
+
this.on("data", this.#onData.bind(this));
|
|
151
204
|
}
|
|
152
205
|
async exchange(type, obj) {
|
|
153
206
|
const _x = this.#xid;
|
|
@@ -174,67 +227,65 @@ class CompanionLinkSocket extends EncryptionAwareConnection {
|
|
|
174
227
|
let data;
|
|
175
228
|
if (this.isEncrypted) {
|
|
176
229
|
const nonce = Buffer.alloc(12);
|
|
177
|
-
nonce.writeBigUInt64LE(BigInt(this.#
|
|
178
|
-
const encrypted = Chacha20.encrypt(this.#
|
|
230
|
+
nonce.writeBigUInt64LE(BigInt(this.#encryptionState.writeCount++), 0);
|
|
231
|
+
const encrypted = Chacha20.encrypt(this.#encryptionState.writeKey, nonce, header, payload);
|
|
179
232
|
data = Buffer.concat([header, encrypted.ciphertext, encrypted.authTag]);
|
|
180
233
|
} else {
|
|
181
234
|
data = Buffer.concat([header, payload]);
|
|
182
235
|
}
|
|
183
|
-
|
|
236
|
+
this.context.logger.raw("[companion-link]", "Sending data frame", this.isEncrypted, Buffer.from(data).toString("hex"), obj);
|
|
184
237
|
try {
|
|
185
238
|
return await this.write(data);
|
|
186
239
|
} catch (err) {
|
|
187
|
-
|
|
240
|
+
this.context.logger.error("[companion-link]", "Error in Companion Link send()", err);
|
|
188
241
|
this.emit("error", err);
|
|
189
242
|
}
|
|
190
243
|
}
|
|
191
|
-
async
|
|
192
|
-
|
|
193
|
-
this.#buffer = Buffer.concat([this.#buffer, buffer]);
|
|
244
|
+
async#onData(data) {
|
|
245
|
+
this.#buffer = Buffer.concat([this.#buffer, data]);
|
|
194
246
|
try {
|
|
195
|
-
while (this.#buffer.byteLength >=
|
|
196
|
-
const header = this.#buffer.subarray(0,
|
|
247
|
+
while (this.#buffer.byteLength >= HEADER_SIZE) {
|
|
248
|
+
const header = this.#buffer.subarray(0, HEADER_SIZE);
|
|
197
249
|
const payloadLength = header.readUintBE(1, 3);
|
|
198
|
-
const totalLength =
|
|
250
|
+
const totalLength = HEADER_SIZE + payloadLength;
|
|
199
251
|
if (this.#buffer.byteLength < totalLength) {
|
|
200
|
-
|
|
252
|
+
this.context.logger.warn("[companion-link]", `Data packet is too short needed=${totalLength} available=${this.#buffer.byteLength} receivedLength=${data.byteLength}`);
|
|
201
253
|
return;
|
|
202
254
|
}
|
|
203
|
-
|
|
204
|
-
|
|
255
|
+
this.context.logger.raw("[companion-link]", `Received frame length=${totalLength} availableLength=${this.#buffer.byteLength} receivedLength=${data.byteLength}`);
|
|
256
|
+
let frame = Buffer.from(this.#buffer.subarray(0, totalLength));
|
|
205
257
|
this.#buffer = this.#buffer.subarray(totalLength);
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
258
|
+
this.context.logger.raw("[companion-link]", `Handle frame, ${this.#buffer.byteLength} bytes left...`);
|
|
259
|
+
if (this.isEncrypted) {
|
|
260
|
+
frame = this.#decrypt(frame);
|
|
261
|
+
}
|
|
262
|
+
const payload = frame.subarray(HEADER_SIZE, totalLength);
|
|
263
|
+
this.#handle(header, payload);
|
|
210
264
|
}
|
|
211
265
|
} catch (err) {
|
|
212
|
-
|
|
266
|
+
this.context.logger.error("[companion-link]", "#onData()", err);
|
|
213
267
|
this.emit("error", err);
|
|
214
268
|
}
|
|
215
269
|
}
|
|
216
|
-
|
|
217
|
-
if (!this.isEncrypted) {
|
|
218
|
-
return data;
|
|
219
|
-
}
|
|
270
|
+
#decrypt(data) {
|
|
220
271
|
const header = data.subarray(0, 4);
|
|
221
272
|
const payloadLength = header.readUintBE(1, 3);
|
|
222
273
|
const payload = data.subarray(4, 4 + payloadLength);
|
|
223
274
|
const authTag = payload.subarray(payload.byteLength - 16);
|
|
224
275
|
const ciphertext = payload.subarray(0, payload.byteLength - 16);
|
|
225
276
|
const nonce = Buffer.alloc(12);
|
|
226
|
-
nonce.writeBigUint64LE(BigInt(this.#
|
|
227
|
-
const decrypted = Chacha20.decrypt(this.#
|
|
277
|
+
nonce.writeBigUint64LE(BigInt(this.#encryptionState.readCount++), 0);
|
|
278
|
+
const decrypted = Chacha20.decrypt(this.#encryptionState.readKey, nonce, header, ciphertext, authTag);
|
|
228
279
|
return Buffer.concat([header, decrypted, authTag]);
|
|
229
280
|
}
|
|
230
|
-
|
|
281
|
+
#handle(header, payload) {
|
|
231
282
|
const type = header.readInt8();
|
|
232
283
|
if (!OPackFrameTypes.includes(type)) {
|
|
233
|
-
|
|
284
|
+
this.context.logger.warn("[companion-link]", "Packet not handled, no opack frame.");
|
|
234
285
|
return;
|
|
235
286
|
}
|
|
236
287
|
payload = OPack.decode(payload);
|
|
237
|
-
|
|
288
|
+
this.context.logger.raw("[companion-link]", "Decoded OPACK", { header, payload });
|
|
238
289
|
if ("_x" in payload) {
|
|
239
290
|
const _x = payload._x;
|
|
240
291
|
if (_x in this.#queue) {
|
|
@@ -256,109 +307,72 @@ class CompanionLinkSocket extends EncryptionAwareConnection {
|
|
|
256
307
|
resolve?.([header, payload]);
|
|
257
308
|
delete this.#queue[_x];
|
|
258
309
|
} else {
|
|
259
|
-
|
|
310
|
+
this.context.logger.warn("[companion-link]", "No handler for message", [header, payload]);
|
|
260
311
|
}
|
|
261
312
|
}
|
|
262
313
|
}
|
|
263
314
|
|
|
264
|
-
// src/verify.ts
|
|
265
|
-
import { AccessoryVerify, hkdf } from "@basmilius/apple-common";
|
|
266
|
-
class CompanionLinkVerify {
|
|
267
|
-
#internal;
|
|
268
|
-
#protocol;
|
|
269
|
-
constructor(protocol) {
|
|
270
|
-
this.#internal = new AccessoryVerify(this.#request.bind(this));
|
|
271
|
-
this.#protocol = protocol;
|
|
272
|
-
}
|
|
273
|
-
async start(credentials) {
|
|
274
|
-
const keys = await this.#internal.start(credentials);
|
|
275
|
-
const accessoryToControllerKey = hkdf({
|
|
276
|
-
hash: "sha512",
|
|
277
|
-
key: keys.sharedSecret,
|
|
278
|
-
length: 32,
|
|
279
|
-
salt: Buffer.alloc(0),
|
|
280
|
-
info: Buffer.from("ServerEncrypt-main")
|
|
281
|
-
});
|
|
282
|
-
const controllerToAccessoryKey = hkdf({
|
|
283
|
-
hash: "sha512",
|
|
284
|
-
key: keys.sharedSecret,
|
|
285
|
-
length: 32,
|
|
286
|
-
salt: Buffer.alloc(0),
|
|
287
|
-
info: Buffer.from("ClientEncrypt-main")
|
|
288
|
-
});
|
|
289
|
-
return {
|
|
290
|
-
accessoryToControllerKey,
|
|
291
|
-
controllerToAccessoryKey,
|
|
292
|
-
pairingId: keys.pairingId,
|
|
293
|
-
sharedSecret: keys.sharedSecret
|
|
294
|
-
};
|
|
295
|
-
}
|
|
296
|
-
async#request(step, data) {
|
|
297
|
-
const frameType = step === "m1" ? FrameType.PV_Start : FrameType.PV_Next;
|
|
298
|
-
const [, response] = await this.#protocol.socket.exchange(frameType, {
|
|
299
|
-
_pd: data,
|
|
300
|
-
_auTy: 4
|
|
301
|
-
});
|
|
302
|
-
if (typeof response !== "object" || response === null) {
|
|
303
|
-
throw new Error("Invalid response from receiver.");
|
|
304
|
-
}
|
|
305
|
-
return response["_pd"];
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
315
|
// src/protocol.ts
|
|
310
|
-
class
|
|
311
|
-
get
|
|
312
|
-
return this.#
|
|
316
|
+
class Protocol {
|
|
317
|
+
get context() {
|
|
318
|
+
return this.#context;
|
|
313
319
|
}
|
|
314
|
-
get
|
|
315
|
-
return this.#
|
|
320
|
+
get discoveryResult() {
|
|
321
|
+
return this.#discoveryResult;
|
|
316
322
|
}
|
|
317
323
|
get pairing() {
|
|
318
324
|
return this.#pairing;
|
|
319
325
|
}
|
|
326
|
+
get stream() {
|
|
327
|
+
return this.#stream;
|
|
328
|
+
}
|
|
320
329
|
get verify() {
|
|
321
330
|
return this.#verify;
|
|
322
331
|
}
|
|
323
|
-
#
|
|
324
|
-
#
|
|
332
|
+
#context;
|
|
333
|
+
#discoveryResult;
|
|
325
334
|
#pairing;
|
|
335
|
+
#stream;
|
|
326
336
|
#verify;
|
|
327
|
-
constructor(
|
|
328
|
-
this.#
|
|
329
|
-
this.#
|
|
330
|
-
this.#
|
|
331
|
-
this.#
|
|
337
|
+
constructor(deviceId, discoveryResult) {
|
|
338
|
+
this.#context = new Context(deviceId.replace(`.${COMPANION_LINK_SERVICE}`, "").replace(".local", ""));
|
|
339
|
+
this.#discoveryResult = discoveryResult;
|
|
340
|
+
this.#stream = new Stream(this.#context, discoveryResult.address, discoveryResult.service.port);
|
|
341
|
+
this.#pairing = new Pairing(this);
|
|
342
|
+
this.#verify = new Verify(this);
|
|
332
343
|
}
|
|
333
344
|
async connect() {
|
|
334
|
-
await this.#
|
|
345
|
+
await this.#stream.connect();
|
|
346
|
+
}
|
|
347
|
+
async destroy() {
|
|
348
|
+
await this.#stream.destroy();
|
|
335
349
|
}
|
|
336
350
|
async disconnect() {
|
|
337
|
-
await this.#
|
|
351
|
+
await this.#stream.disconnect();
|
|
338
352
|
}
|
|
339
353
|
async fetchMediaControlStatus() {
|
|
340
|
-
await this.#
|
|
354
|
+
await this.#stream.exchange(FrameType.E_OPACK, {
|
|
341
355
|
_i: "FetchMediaControlStatus",
|
|
342
356
|
_t: MessageType.Request,
|
|
343
357
|
_c: {}
|
|
344
358
|
});
|
|
345
359
|
}
|
|
346
360
|
async fetchNowPlayingInfo() {
|
|
347
|
-
await this.#
|
|
361
|
+
await this.#stream.exchange(FrameType.E_OPACK, {
|
|
348
362
|
_i: "FetchCurrentNowPlayingInfoEvent",
|
|
349
363
|
_t: MessageType.Request,
|
|
350
364
|
_c: {}
|
|
351
365
|
});
|
|
352
366
|
}
|
|
353
367
|
async fetchSupportedActions() {
|
|
354
|
-
await this.#
|
|
368
|
+
await this.#stream.exchange(FrameType.E_OPACK, {
|
|
355
369
|
_i: "FetchSupportedActionsEvent",
|
|
356
370
|
_t: MessageType.Request,
|
|
357
371
|
_c: {}
|
|
358
372
|
});
|
|
359
373
|
}
|
|
360
374
|
async getAttentionState() {
|
|
361
|
-
const [, payload] = await this.#
|
|
375
|
+
const [, payload] = await this.#stream.exchange(FrameType.E_OPACK, {
|
|
362
376
|
_i: "FetchAttentionState",
|
|
363
377
|
_t: MessageType.Request,
|
|
364
378
|
_c: {}
|
|
@@ -367,7 +381,7 @@ class CompanionLink {
|
|
|
367
381
|
return convertAttentionState(_c.state);
|
|
368
382
|
}
|
|
369
383
|
async getLaunchableApps() {
|
|
370
|
-
const [, payload] = await this.#
|
|
384
|
+
const [, payload] = await this.#stream.exchange(FrameType.E_OPACK, {
|
|
371
385
|
_i: "FetchLaunchableApplicationsEvent",
|
|
372
386
|
_t: MessageType.Request,
|
|
373
387
|
_c: {}
|
|
@@ -379,7 +393,7 @@ class CompanionLink {
|
|
|
379
393
|
}));
|
|
380
394
|
}
|
|
381
395
|
async getSiriRemoteInfo() {
|
|
382
|
-
const [, payload] = await this.#
|
|
396
|
+
const [, payload] = await this.#stream.exchange(FrameType.E_OPACK, {
|
|
383
397
|
_i: "FetchSiriRemoteInfo",
|
|
384
398
|
_t: MessageType.Request,
|
|
385
399
|
_c: {}
|
|
@@ -387,7 +401,7 @@ class CompanionLink {
|
|
|
387
401
|
return Plist.parse(Buffer.from(payload["_c"]["SiriRemoteInfoKey"]).buffer);
|
|
388
402
|
}
|
|
389
403
|
async getUserAccounts() {
|
|
390
|
-
const [, payload] = await this.#
|
|
404
|
+
const [, payload] = await this.#stream.exchange(FrameType.E_OPACK, {
|
|
391
405
|
_i: "FetchUserAccountsEvent",
|
|
392
406
|
_t: MessageType.Request,
|
|
393
407
|
_c: {}
|
|
@@ -399,7 +413,7 @@ class CompanionLink {
|
|
|
399
413
|
}));
|
|
400
414
|
}
|
|
401
415
|
async hidCommand(command, down = false) {
|
|
402
|
-
await this.#
|
|
416
|
+
await this.#stream.exchange(FrameType.E_OPACK, {
|
|
403
417
|
_i: "_hidC",
|
|
404
418
|
_t: MessageType.Request,
|
|
405
419
|
_c: {
|
|
@@ -409,7 +423,7 @@ class CompanionLink {
|
|
|
409
423
|
});
|
|
410
424
|
}
|
|
411
425
|
async launchApp(bundleId) {
|
|
412
|
-
await this.#
|
|
426
|
+
await this.#stream.exchange(FrameType.E_OPACK, {
|
|
413
427
|
_i: "_launchApp",
|
|
414
428
|
_t: MessageType.Request,
|
|
415
429
|
_c: {
|
|
@@ -418,7 +432,7 @@ class CompanionLink {
|
|
|
418
432
|
});
|
|
419
433
|
}
|
|
420
434
|
async launchUrl(url) {
|
|
421
|
-
await this.#
|
|
435
|
+
await this.#stream.exchange(FrameType.E_OPACK, {
|
|
422
436
|
_i: "_launchApp",
|
|
423
437
|
_t: MessageType.Request,
|
|
424
438
|
_c: {
|
|
@@ -427,7 +441,7 @@ class CompanionLink {
|
|
|
427
441
|
});
|
|
428
442
|
}
|
|
429
443
|
async mediaControlCommand(command, content) {
|
|
430
|
-
const [, payload] = await this.#
|
|
444
|
+
const [, payload] = await this.#stream.exchange(FrameType.E_OPACK, {
|
|
431
445
|
_i: "_mcc",
|
|
432
446
|
_t: MessageType.Request,
|
|
433
447
|
_c: {
|
|
@@ -457,7 +471,7 @@ class CompanionLink {
|
|
|
457
471
|
}
|
|
458
472
|
}
|
|
459
473
|
async switchUserAccount(accountId) {
|
|
460
|
-
await this.#
|
|
474
|
+
await this.#stream.exchange(FrameType.E_OPACK, {
|
|
461
475
|
_i: "SwitchUserAccountEvent",
|
|
462
476
|
_t: MessageType.Request,
|
|
463
477
|
_c: {
|
|
@@ -466,8 +480,8 @@ class CompanionLink {
|
|
|
466
480
|
});
|
|
467
481
|
}
|
|
468
482
|
async _subscribe(event, fn) {
|
|
469
|
-
this.#
|
|
470
|
-
await this.#
|
|
483
|
+
this.#stream.on(event, fn);
|
|
484
|
+
await this.#stream.send(FrameType.E_OPACK, {
|
|
471
485
|
_i: "_interest",
|
|
472
486
|
_t: MessageType.Event,
|
|
473
487
|
_c: {
|
|
@@ -476,13 +490,13 @@ class CompanionLink {
|
|
|
476
490
|
});
|
|
477
491
|
}
|
|
478
492
|
async _unsubscribe(event, fn) {
|
|
479
|
-
if (!this.
|
|
493
|
+
if (!this.#stream.isConnected) {
|
|
480
494
|
return;
|
|
481
495
|
}
|
|
482
496
|
if (fn) {
|
|
483
|
-
this.#
|
|
497
|
+
this.#stream.off(event, fn);
|
|
484
498
|
}
|
|
485
|
-
await this.#
|
|
499
|
+
await this.#stream.send(FrameType.E_OPACK, {
|
|
486
500
|
_i: "_interest",
|
|
487
501
|
_t: MessageType.Event,
|
|
488
502
|
_c: {
|
|
@@ -491,7 +505,7 @@ class CompanionLink {
|
|
|
491
505
|
});
|
|
492
506
|
}
|
|
493
507
|
async _sessionStart() {
|
|
494
|
-
const [, payload] = await this.#
|
|
508
|
+
const [, payload] = await this.#stream.exchange(FrameType.E_OPACK, {
|
|
495
509
|
_i: "_sessionStart",
|
|
496
510
|
_t: MessageType.Request,
|
|
497
511
|
_btHP: false,
|
|
@@ -504,7 +518,7 @@ class CompanionLink {
|
|
|
504
518
|
return objectOrFail(payload);
|
|
505
519
|
}
|
|
506
520
|
async _systemInfo(pairingId) {
|
|
507
|
-
const [, payload] = await this.#
|
|
521
|
+
const [, payload] = await this.#stream.exchange(FrameType.E_OPACK, {
|
|
508
522
|
_i: "_systemInfo",
|
|
509
523
|
_t: MessageType.Request,
|
|
510
524
|
_btHP: false,
|
|
@@ -554,7 +568,7 @@ class CompanionLink {
|
|
|
554
568
|
return objectOrFail(payload);
|
|
555
569
|
}
|
|
556
570
|
async _tiStart() {
|
|
557
|
-
const [, payload] = await this.#
|
|
571
|
+
const [, payload] = await this.#stream.exchange(FrameType.E_OPACK, {
|
|
558
572
|
_i: "_tiStart",
|
|
559
573
|
_t: MessageType.Request,
|
|
560
574
|
_btHP: false,
|
|
@@ -563,7 +577,7 @@ class CompanionLink {
|
|
|
563
577
|
return objectOrFail(payload);
|
|
564
578
|
}
|
|
565
579
|
async _touchStart() {
|
|
566
|
-
const [, payload] = await this.#
|
|
580
|
+
const [, payload] = await this.#stream.exchange(FrameType.E_OPACK, {
|
|
567
581
|
_i: "_touchStart",
|
|
568
582
|
_t: MessageType.Request,
|
|
569
583
|
_btHP: false,
|
|
@@ -576,7 +590,7 @@ class CompanionLink {
|
|
|
576
590
|
return objectOrFail(payload);
|
|
577
591
|
}
|
|
578
592
|
async _tvrcSessionStart() {
|
|
579
|
-
const [, payload] = await this.#
|
|
593
|
+
const [, payload] = await this.#stream.exchange(FrameType.E_OPACK, {
|
|
580
594
|
_i: "TVRCSessionStart",
|
|
581
595
|
_t: MessageType.Request,
|
|
582
596
|
_btHP: false,
|
|
@@ -590,12 +604,14 @@ function objectOrFail(obj) {
|
|
|
590
604
|
if (typeof obj === "object") {
|
|
591
605
|
return obj;
|
|
592
606
|
}
|
|
593
|
-
reporter2.error("Expected an object.", { obj });
|
|
594
607
|
throw new Error("Expected an object.");
|
|
595
608
|
}
|
|
596
609
|
export {
|
|
597
610
|
convertAttentionState,
|
|
611
|
+
Verify,
|
|
612
|
+
Stream,
|
|
613
|
+
Protocol,
|
|
614
|
+
Pairing,
|
|
598
615
|
MediaControlCommand,
|
|
599
|
-
HidCommand
|
|
600
|
-
CompanionLink
|
|
616
|
+
HidCommand
|
|
601
617
|
};
|
package/dist/pairing.d.ts
CHANGED
|
@@ -1,10 +1,19 @@
|
|
|
1
|
-
import { type AccessoryCredentials, type AccessoryKeys, AccessoryPair } from "@basmilius/apple-common";
|
|
1
|
+
import { type AccessoryCredentials, type AccessoryKeys, AccessoryPair, AccessoryVerify } from "@basmilius/apple-common";
|
|
2
2
|
import type Protocol from "./protocol";
|
|
3
|
-
|
|
3
|
+
import type Stream from "./stream";
|
|
4
|
+
export declare class Pairing {
|
|
4
5
|
#private;
|
|
5
6
|
get internal(): AccessoryPair;
|
|
7
|
+
get stream(): Stream;
|
|
6
8
|
constructor(protocol: Protocol);
|
|
7
9
|
start(): Promise<void>;
|
|
8
10
|
pin(askPin: () => Promise<string>): Promise<AccessoryCredentials>;
|
|
9
11
|
transient(): Promise<AccessoryKeys>;
|
|
10
12
|
}
|
|
13
|
+
export declare class Verify {
|
|
14
|
+
#private;
|
|
15
|
+
get internal(): AccessoryVerify;
|
|
16
|
+
get stream(): Stream;
|
|
17
|
+
constructor(protocol: Protocol);
|
|
18
|
+
start(credentials: AccessoryCredentials): Promise<AccessoryKeys>;
|
|
19
|
+
}
|
package/dist/protocol.d.ts
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
import { type DiscoveryResult } from "@basmilius/apple-common";
|
|
2
|
-
import type { AttentionState, ButtonPressType, LaunchableApp, UserAccount } from "./types";
|
|
1
|
+
import { Context, type DiscoveryResult } from "@basmilius/apple-common";
|
|
3
2
|
import { type HidCommandKey, type MediaControlCommandKey } from "./const";
|
|
4
|
-
import Pairing from "./pairing";
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
export default class
|
|
3
|
+
import { Pairing, Verify } from "./pairing";
|
|
4
|
+
import type { AttentionState, ButtonPressType, LaunchableApp, UserAccount } from "./types";
|
|
5
|
+
import Stream from "./stream";
|
|
6
|
+
export default class Protocol {
|
|
8
7
|
#private;
|
|
9
|
-
get
|
|
10
|
-
get
|
|
8
|
+
get context(): Context;
|
|
9
|
+
get discoveryResult(): DiscoveryResult;
|
|
11
10
|
get pairing(): Pairing;
|
|
11
|
+
get stream(): Stream;
|
|
12
12
|
get verify(): Verify;
|
|
13
|
-
constructor(
|
|
13
|
+
constructor(deviceId: string, discoveryResult: DiscoveryResult);
|
|
14
14
|
connect(): Promise<void>;
|
|
15
|
+
destroy(): Promise<void>;
|
|
15
16
|
disconnect(): Promise<void>;
|
|
16
17
|
fetchMediaControlStatus(): Promise<void>;
|
|
17
18
|
fetchNowPlayingInfo(): Promise<void>;
|
package/dist/stream.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type Context, EncryptionAwareConnection } from "@basmilius/apple-common";
|
|
2
|
+
export default class Stream extends EncryptionAwareConnection<Record<string, [unknown]>> {
|
|
3
|
+
#private;
|
|
4
|
+
constructor(context: Context, address: string, port: number);
|
|
5
|
+
exchange(type: number, obj: Record<string, unknown>): Promise<[number, unknown]>;
|
|
6
|
+
send(type: number, obj: Record<string, unknown>): Promise<void>;
|
|
7
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@basmilius/apple-companion-link",
|
|
3
3
|
"description": "Implementation of Apple's Companion Link in Node.js.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": {
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
}
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@basmilius/apple-common": "0.
|
|
44
|
-
"@basmilius/apple-encoding": "0.
|
|
43
|
+
"@basmilius/apple-common": "0.2.0",
|
|
44
|
+
"@basmilius/apple-encoding": "0.2.0"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@basmilius/tools": "^2.23.0",
|
package/dist/socket.d.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { EncryptionAwareConnection } from "@basmilius/apple-common";
|
|
2
|
-
export default class CompanionLinkSocket extends EncryptionAwareConnection<Record<string, [unknown]>> {
|
|
3
|
-
#private;
|
|
4
|
-
constructor(address: string, port: number);
|
|
5
|
-
exchange(type: number, obj: Record<string, unknown>): Promise<[number, unknown]>;
|
|
6
|
-
send(type: number, obj: Record<string, unknown>): Promise<void>;
|
|
7
|
-
onData(buffer: Buffer): Promise<void>;
|
|
8
|
-
}
|
package/dist/verify.d.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { type AccessoryCredentials, type AccessoryKeys } from "@basmilius/apple-common";
|
|
2
|
-
import type Protocol from "./protocol";
|
|
3
|
-
export default class CompanionLinkVerify {
|
|
4
|
-
#private;
|
|
5
|
-
constructor(protocol: Protocol);
|
|
6
|
-
start(credentials: AccessoryCredentials): Promise<AccessoryKeys>;
|
|
7
|
-
}
|
|
File without changes
|