181 lines
6.0 KiB
JavaScript
181 lines
6.0 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
const axios_1 = require("axios");
|
|
const events_1 = require("events");
|
|
const command_requester_1 = require("@jibo/command-requester");
|
|
const constants_1 = require("./constants");
|
|
const fs_1 = require("fs");
|
|
function readIfExists(path) {
|
|
if (!path) {
|
|
return undefined;
|
|
}
|
|
try {
|
|
return fs_1.readFileSync(path, "utf8");
|
|
}
|
|
catch (_a) {
|
|
return undefined;
|
|
}
|
|
}
|
|
/**
|
|
* An API handle to a Jibo robot, used to request commands
|
|
* @class Robot
|
|
* @hideconstructor
|
|
*/
|
|
class Robot extends events_1.EventEmitter {
|
|
/** @private */
|
|
constructor(
|
|
/**
|
|
* The "friendly serial name" from the bottom of the robot,
|
|
* e.g. My-Friendly-Robot-Name
|
|
*/
|
|
serialName, tokenType, accessToken) {
|
|
super();
|
|
this.serialName = serialName;
|
|
this.tokenType = tokenType;
|
|
this.accessToken = accessToken;
|
|
}
|
|
/**
|
|
* `true` if the app is currently connected to this robot
|
|
* @method Robot#connected
|
|
* @returns {boolean}
|
|
*/
|
|
get connected() {
|
|
return this._connected;
|
|
}
|
|
/**
|
|
* A handle to the command requester for this robot
|
|
* @method Robot#requester
|
|
* @returns {CommandRequester}
|
|
*/
|
|
get requester() {
|
|
return this._requester;
|
|
}
|
|
/**
|
|
* Configure direct/offline connection values to avoid cloud lookup.
|
|
* Accepts inline PEM values or *_PATH values.
|
|
*/
|
|
configureDirectConnection(config) {
|
|
if (!config) {
|
|
return;
|
|
}
|
|
console.log("[JIBO_BYPASS] configureDirectConnection(): applying offline connection config");
|
|
if (config.ip) {
|
|
this._ip = config.ip;
|
|
}
|
|
if (config.skipTls) {
|
|
this._skipTls = true;
|
|
console.log("[JIBO_BYPASS] configureDirectConnection(): TLS disabled for direct websocket connection");
|
|
}
|
|
if (config.fingerprint) {
|
|
this._fingerprint = config.fingerprint;
|
|
}
|
|
const cert = config.certPem || readIfExists(config.certPath);
|
|
if (cert) {
|
|
this._certificate = cert;
|
|
}
|
|
const key = config.keyPem || readIfExists(config.keyPath);
|
|
if (key) {
|
|
this._key = key;
|
|
}
|
|
}
|
|
/**
|
|
* Establish a connection to this robot
|
|
* @method Robot.connect
|
|
*/
|
|
async connect() {
|
|
if (this.connected) {
|
|
await this.disconnect();
|
|
}
|
|
if (!this._ip) {
|
|
await this._requestCertificate();
|
|
await this._retrieveCertificate();
|
|
}
|
|
if (!this._skipTls && (!this._certificate || !this._key)) {
|
|
console.log("[JIBO_BYPASS] connect(): missing direct credentials", {
|
|
hasIp: !!this._ip,
|
|
hasCert: !!this._certificate,
|
|
hasKey: !!this._key,
|
|
hasFingerprint: !!this._fingerprint,
|
|
});
|
|
throw new Error("Missing certificate/key for direct robot connection");
|
|
}
|
|
const options = {
|
|
port: constants_1.PORT,
|
|
perMessageDeflate: false,
|
|
};
|
|
if (!this._skipTls) {
|
|
options.key = this._key;
|
|
options.cert = this._certificate;
|
|
options.rejectUnauthorized = false;
|
|
options.fingerprint = this._fingerprint;
|
|
}
|
|
this._requester = new command_requester_1.CommandRequester();
|
|
this._requester.disconnected.on(data => {
|
|
this._connected = false;
|
|
this.emit("disconnected", data);
|
|
this.emit("status", "disconnected");
|
|
});
|
|
return this._requester.connect(this._ip, options)
|
|
.then(() => {
|
|
this._connected = true;
|
|
this.emit("status", "connected");
|
|
})
|
|
.catch(err => { throw new Error(err.message); });
|
|
}
|
|
/**
|
|
* Disconnect from this robot
|
|
* @method Robot#disconnect
|
|
*/
|
|
disconnect() {
|
|
// Don't throw an error if this is called in a late cleanup and this
|
|
// falls out of scope
|
|
if (this) {
|
|
if (this._requester) {
|
|
this._requester.disconnect();
|
|
}
|
|
this._certificate = null;
|
|
this._connected = false;
|
|
this._fingerprint = null;
|
|
this._ip = null;
|
|
this._key = null;
|
|
this._requester = null;
|
|
this.emit("status", "disconnected");
|
|
}
|
|
}
|
|
async _requestCertificate() {
|
|
const certificateCreationUri = `https://${constants_1.ENDPOINT}/rom/v1/certificates`;
|
|
const body = { friendlyId: this.serialName };
|
|
const headers = { Authorization: `${this.tokenType} ${this.accessToken}` };
|
|
await axios_1.default.post(certificateCreationUri, body, { headers })
|
|
.then(() => this.emit("status", "certificateRequested"))
|
|
.catch(err => { throw new Error(err.message); });
|
|
}
|
|
async _retrieveCertificate() {
|
|
const certificateRetrievalUri = `https://${constants_1.ENDPOINT}/rom/v1/certificates/client?friendlyId=${this.serialName}`;
|
|
const headers = { Authorization: `${this.tokenType} ${this.accessToken}` };
|
|
this._ip = null;
|
|
let numTries = 0;
|
|
while (!this._ip && numTries < constants_1.MAX_CERT_TRIES) {
|
|
try {
|
|
await axios_1.default.get(certificateRetrievalUri, {
|
|
headers,
|
|
timeout: 5000,
|
|
}).then(res => {
|
|
this._certificate = res.data.data.cert;
|
|
this._fingerprint = res.data.data.fingerprint;
|
|
this._ip = res.data.data.payload.ipAddress;
|
|
this._key = res.data.data.private;
|
|
});
|
|
}
|
|
catch (err) {
|
|
numTries++;
|
|
}
|
|
}
|
|
if (!this._ip) {
|
|
throw new Error("Failed to retrieve certificate");
|
|
}
|
|
this.emit("status", "certificateReceived");
|
|
}
|
|
}
|
|
exports.Robot = Robot;
|
|
//# sourceMappingURL=Robot.js.map
|