@poki/cli
Version:
Poki for Developers command line utility
526 lines (510 loc) • 24.9 kB
JavaScript
;
var fs = require('fs');
var yargs = require('yargs');
var archiver = require('archiver');
var http = require('http');
var https = require('https');
var path = require('path');
var open = require('open');
var os = require('os');
var FormData = require('form-data');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var yargs__default = /*#__PURE__*/_interopDefaultLegacy(yargs);
var archiver__default = /*#__PURE__*/_interopDefaultLegacy(archiver);
var open__default = /*#__PURE__*/_interopDefaultLegacy(open);
var FormData__default = /*#__PURE__*/_interopDefaultLegacy(FormData);
/******************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
var __assign = function() {
__assign = Object.assign || function __assign(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
function __awaiter(thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
}
function __generator(thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
}
function createZip(filename, dir) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, new Promise(function (resolve, reject) {
var archive = archiver__default["default"]('zip', { zlib: { level: 6 } });
var stream = fs.createWriteStream(filename);
archive
.directory(dir, false)
.on('error', function (err) { return reject(err); })
.pipe(stream);
stream.on('close', function () { return resolve(); });
stream.on('error', function (err) { return reject(err); });
archive.finalize()["catch"](function (err) {
reject(err);
});
})];
case 1: return [2 /*return*/, _a.sent()];
}
});
});
}
function getConfigDir() {
var defaultConfigDir = path.join(os.homedir(), '.config', 'poki');
if (process.platform === 'win32') {
return process.env.LOCALAPPDATA !== undefined ? path.join(process.env.LOCALAPPDATA, 'Poki') : defaultConfigDir;
}
else if (process.env.XDG_CONFIG_HOME !== undefined) {
return path.join(process.env.XDG_CONFIG_HOME, 'poki');
}
else {
return defaultConfigDir;
}
}
function exchange(exchangeToken) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, new Promise(function (resolve, reject) {
var req = https.request({
hostname: 'auth.poki.io',
port: 443,
path: '/auth/exchange',
method: 'POST',
headers: { 'Content-Type': 'application/json' }
}, function (res) {
var data = '';
res.on('data', function (chunk) {
data += chunk;
});
res.on('end', function () {
if (res.statusCode !== 200) {
reject(data);
}
else {
resolve(JSON.parse(data));
}
});
});
req.on('error', function (error) {
reject(error);
});
req.write(JSON.stringify({ exchange_token: exchangeToken }));
req.end();
})];
case 1: return [2 /*return*/, _a.sent()];
}
});
});
}
function refresh(config) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
console.log('refreshing authentication...');
return [4 /*yield*/, new Promise(function (resolve, reject) {
var req = https.request({
hostname: 'auth.poki.io',
port: 443,
path: '/auth/refresh',
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
}, function (res) {
var data = '';
res.on('data', function (chunk) {
data += chunk;
});
res.on('end', function () {
if (res.statusCode !== 200) {
reject(data);
}
else {
var configPath = path.join(getConfigDir(), 'auth.json');
var body = JSON.parse(data);
config.access_token = body.access_token;
fs.writeFileSync(configPath, JSON.stringify(config), 'ascii');
// Make sure the file isn't readable for everyone.
fs.chmodSync(configPath, '600');
resolve(config);
}
});
});
req.on('error', function (error) {
reject(error);
});
req.write(JSON.stringify({ refresh_token: config.refresh_token }));
req.end();
})];
case 1: return [2 /*return*/, _a.sent()];
}
});
});
}
function auth(force) {
if (force === void 0) { force = false; }
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, new Promise(function (resolve, reject) {
var configDir = getConfigDir();
var configPath = path.join(configDir, 'auth.json');
var config;
if (!force) {
try {
config = JSON.parse(fs.readFileSync(configPath, 'ascii'));
}
catch (e) {
// Ignore.
}
}
if (typeof process.env.POKI_ACCESS_TOKEN === 'string') {
console.warn('POKI_ACCESS_TOKEN has been deprecated, please use POKI_UPLOAD_TOKEN');
config = __assign(__assign({}, config), { access_type: 'Token', access_token: process.env.POKI_ACCESS_TOKEN });
}
if (typeof process.env.POKI_UPLOAD_TOKEN === 'string') {
config = __assign(__assign({}, config), { access_type: 'Token', access_token: process.env.POKI_UPLOAD_TOKEN });
}
if (config !== undefined) {
resolve(config);
return;
}
console.log('authentication required, opening browser...');
if (!fs.existsSync(configDir)) {
try {
fs.mkdirSync(configDir, { recursive: true });
}
catch (err) {
reject(err);
return;
}
}
var server = http.createServer(function (req, res) {
if (req.method === 'OPTIONS') {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
res.writeHead(200);
res.end();
return;
}
var q = new URL(req.url, 'http://localhost');
if (q.pathname === '/favicon.ico') {
res.writeHead(404);
res.end();
return;
}
var exchangeToken = q.searchParams.get('exchange_token');
if (exchangeToken !== null) {
res.setHeader('Content-Type', 'text/html');
res.writeHead(200);
res.end('You can close this window and return to your terminal');
server.close();
exchange(exchangeToken).then(function (config) {
fs.writeFileSync(configPath, JSON.stringify(config), 'ascii');
// Make sure the file isn't readable for everyone.
fs.chmodSync(configPath, '600');
resolve(config);
})["catch"](function (err) {
reject(err);
});
}
else {
res.setHeader('Content-Type', 'text/plain');
res.writeHead(200);
res.end('missing exchange_token');
server.close();
reject(Error('missing exchange_token'));
}
});
server.on('error', function (err) {
reject(err);
});
server.listen(0, function () {
var address = server.address();
if (address === null || typeof address === 'string') {
return;
}
open__default["default"]("https://app.poki.dev/signin/?cli=".concat(encodeURIComponent("http://localhost:".concat(address.port))))["catch"](function (err) {
reject(err);
});
});
})];
case 1: return [2 /*return*/, _a.sent()];
}
});
});
}
function doit(gameId, filename, name, notes, makePublic, disableImageCompression, config) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, new Promise(function (resolve, reject) {
var _a, _b, _c;
if (config.access_token === undefined) {
return reject(new Error('No access token found'));
}
var stat = fs.statSync(filename);
var form = new FormData__default["default"]();
form.append('file', fs.readFileSync(filename), {
filepath: path.basename(filename),
knownLength: stat.size,
contentType: 'application/zip'
});
if (name !== filename) {
form.append('label', name);
}
if (notes !== undefined) {
form.append('notes', notes);
}
if (makePublic) {
form.append('make-public', 'true');
}
if (disableImageCompression) {
form.append('disable-image-compression', 'true');
}
console.log('uploading...');
var buffer = form.getBuffer();
var path$1 = (_a = process.env.API_PATH) !== null && _a !== void 0 ? _a : "/games/".concat(gameId, "/versions");
var req = https.request({
hostname: '34.111.107.149',
port: 443,
path: path$1,
method: 'POST',
headers: __assign({ Host: 'devs-api.poki.io', Authorization: "".concat((_b = config.access_type) !== null && _b !== void 0 ? _b : 'Bearer', " ").concat((_c = config.access_token) !== null && _c !== void 0 ? _c : ''), 'Content-Length': buffer.length, 'User-Agent': 'poki-cli' }, form.getHeaders())
}, function (res) {
var data = [];
res.on('data', function (chunk) {
data.push(chunk);
});
res.on('end', function () {
resolve({
statusCode: res.statusCode,
data: Buffer.concat(data).toString()
});
});
});
req.on('error', function (error) {
reject(error);
});
req.write(buffer, 'binary');
req.end();
})];
case 1: return [2 /*return*/, _a.sent()];
}
});
});
}
function postToP4D(gameId, filename, name, notes, makePublic, disableImageCompression) {
return __awaiter(this, void 0, void 0, function () {
var config, response;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, auth()];
case 1:
config = _a.sent();
return [4 /*yield*/, doit(gameId, filename, name, notes, makePublic, disableImageCompression, config)];
case 2:
response = _a.sent();
if (!(response.statusCode === 401 && config.access_type !== 'Token')) return [3 /*break*/, 9];
_a.label = 3;
case 3:
_a.trys.push([3, 5, , 7]);
return [4 /*yield*/, refresh(config)];
case 4:
config = _a.sent();
return [3 /*break*/, 7];
case 5:
_a.sent();
return [4 /*yield*/, auth(true)];
case 6:
config = _a.sent();
return [3 /*break*/, 7];
case 7: return [4 /*yield*/, doit(gameId, filename, name, notes, makePublic, disableImageCompression, config)];
case 8:
response = _a.sent();
_a.label = 9;
case 9:
if (response.statusCode !== 201) {
throw Error(JSON.stringify(response));
}
else {
return [2 /*return*/, JSON.parse(response.data)];
}
}
});
});
}
var _a, _b;
function upload(gameId, buildDir, filename, name, notes, makePublic, disableImageCompression) {
return __awaiter(this, void 0, void 0, function () {
var notesWithPostfix, data, err_1;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, createZip(filename, buildDir)];
case 1:
_a.sent();
notesWithPostfix = ((notes !== null && notes !== void 0 ? notes : '') + '\n\nUploaded using poki-cli').trim();
_a.label = 2;
case 2:
_a.trys.push([2, 4, , 5]);
return [4 /*yield*/, postToP4D(gameId, filename, name, notesWithPostfix, makePublic, disableImageCompression)];
case 3:
data = _a.sent();
console.log("\nVersion uploaded successfully\n\nYour build is still processing, once that is done the following links will be available:\n Inspector: https://inspector.poki.dev/?game=poki-".concat(data.id, "\n Preview: https://poki.com/en/preview/").concat(data.game_id, "/").concat(data.id, "\n"));
return [3 /*break*/, 5];
case 4:
err_1 = _a.sent();
console.error(err_1);
return [3 /*break*/, 5];
case 5:
fs.unlinkSync(filename);
process.exit(0);
return [2 /*return*/];
}
});
});
}
function init(gameId, buildDir) {
fs.writeFileSync('poki.json', JSON.stringify({
game_id: gameId,
build_dir: buildDir
}, null, 2) + '\n', 'ascii');
}
var config = {};
try {
config = JSON.parse(fs.readFileSync('poki.json', 'ascii'));
}
catch (ignore) {
try {
var packagejson = JSON.parse(fs.readFileSync('package.json', 'ascii'));
if (typeof packagejson.poki === 'object') {
config = packagejson.poki;
}
}
catch (ignore) { }
}
var filename = "".concat(new Date(Date.now() - new Date().getTimezoneOffset() * 60000).toISOString().split('.')[0].replace('T', '-').replace(/:/g, ''), ".zip");
var argv = yargs__default["default"](process.argv.slice(2))
.command('init', 'Create a poki.json configuration file', function (yargs) {
return yargs
.option('game', {
alias: 'g',
describe: 'Poki for Developers game ID',
demandOption: true,
type: 'string'
})
.option('build-dir', {
alias: 'b',
describe: 'Directory to upload',
"default": 'dist',
type: 'string'
});
})
.command('upload', 'Upload a new version to Poki for Developers', function (yargs) {
var _a;
return yargs
.option('game', {
alias: 'g',
describe: 'Poki for Developers game ID',
demandOption: config.game_id !== undefined,
"default": config.game_id,
type: 'string'
})
.option('build-dir', {
alias: 'b',
describe: 'Directory to upload',
"default": (_a = config.build_dir) !== null && _a !== void 0 ? _a : 'dist',
type: 'string'
})
.option('name', {
alias: 'n',
describe: 'Version name',
"default": filename,
type: 'string'
})
.option('notes', {
alias: 'o',
describe: 'Version notes',
type: 'string'
})
.option('make-public', {
alias: 'l',
describe: 'Make version public after upload',
"default": false,
type: 'boolean'
})
.option('disable-image-compression', {
alias: 'i',
describe: 'Disable image compression',
"default": false,
type: 'boolean'
});
})
.demandCommand(1)
.example([
["$0 init --game ".concat((_a = config.game_id) !== null && _a !== void 0 ? _a : 'f85c728f-1055-4777-92fa-841eb40ee723', " --build-dir ").concat((_b = config.build_dir) !== null && _b !== void 0 ? _b : 'dist')],
['$0 upload --name "New Version Name"'],
['$0 upload --name "$(git rev-parse --short HEAD)" --notes "$(git log -1 --pretty=%B)"']
])
.help('h')
.alias('h', 'help')
.wrap(94) // Make sure our examples fit.
.parseSync();
if (argv._.includes('init')) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
init(argv.game, argv.buildDir);
}
if (argv._.includes('upload')) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
upload(argv.game, argv.buildDir, filename, argv.name, argv.notes, argv['make-public'], argv['disable-image-compression'])["catch"](function (err) {
console.error(err);
});
}