121 lines
4.5 KiB
JavaScript
Raw Normal View History

import { mkdtempSync, rmSync, existsSync, readFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { resolve, join } from 'node:path';
import { spawnSync } from 'node:child_process';
const args = new Set(process.argv.slice(2));
const checkOnly = args.has('--check');
const repoRoot = resolve(new URL('../..', import.meta.url).pathname);
const matrixCli = resolve(repoRoot, 'packages', 'cli', 'dist', 'bin', 'matrix.js');
function envString(...names) {
for (const name of names) {
const value = process.env[name];
if (typeof value === 'string' && value.trim()) return value.trim();
}
return undefined;
}
function run(command, commandArgs, options = {}) {
const result = spawnSync(command, commandArgs, {
cwd: options.cwd ?? repoRoot,
stdio: options.stdio ?? 'pipe',
env: process.env,
encoding: 'utf8',
});
if (result.error) throw result.error;
if (result.status !== 0) {
throw new Error(`${command} ${commandArgs.join(' ')} failed with exit ${result.status}\n${result.stderr}${result.stdout}`);
}
return result;
}
function resolveConfig() {
const key = envString('MATRIX_API_KEY', 'HIVECAST_API_KEY', 'HIVECAST_TEST_KEY');
const cloud = envString('MATRIX_CLOUD', 'HIVECAST_CLOUD', 'HIVECAST_TEST_CLOUD');
const space = envString('MATRIX_SPACE', 'HIVECAST_SPACE', 'HIVECAST_TEST_SPACE');
const provided = [key, cloud, space].filter(Boolean).length;
if (provided > 0 && provided < 3) {
throw new Error('Provide MATRIX_API_KEY, MATRIX_CLOUD, and MATRIX_SPACE together');
}
return key && cloud && space
? { key, cloud: cloud.replace(/\/+$/g, ''), space }
: null;
}
async function validateHiveCastKey(config) {
const response = await fetch(`${config.cloud}/api/install/key-info`, {
method: 'POST',
headers: {
authorization: `Bearer ${config.key}`,
'content-type': 'application/json',
},
body: JSON.stringify({ space: config.space }),
});
const text = await response.text();
if (!response.ok) {
throw new Error(`HiveCast key validation failed (${response.status}): ${text}`);
}
const body = text.trim() ? JSON.parse(text) : {};
if (body?.ok !== true) {
throw new Error(`HiveCast key validation did not return ok:true: ${text}`);
}
return {
keyId: body.key?.id ?? body.id ?? null,
fingerprint: body.key?.fingerprint ?? body.fingerprint ?? null,
};
}
const config = resolveConfig();
if (!config) {
const message = 'HiveCast demo requires MATRIX_API_KEY, MATRIX_CLOUD, and MATRIX_SPACE';
if (checkOnly) {
console.log(JSON.stringify({ ok: true, demo: 'hivecast-login', credentialsRequired: true, message }, null, 2));
process.exit(0);
}
throw new Error(message);
}
if (checkOnly) {
console.log(JSON.stringify({ ok: true, demo: 'hivecast-login', credentialsAvailable: true }, null, 2));
process.exit(0);
}
const demoHome = process.env.MATRIX_DEMO_HOME
? resolve(process.env.MATRIX_DEMO_HOME)
: mkdtempSync(join(tmpdir(), 'hivecast-sdk-login.'));
const keep = process.env.MATRIX_DEMO_KEEP === '1' || Boolean(process.env.MATRIX_DEMO_HOME);
try {
const validation = await validateHiveCastKey(config);
run(process.execPath, [
matrixCli, 'configure',
'--dir', demoHome,
'--key', config.key,
'--cloud', config.cloud,
'--space', config.space,
]);
const credentialPath = join(demoHome, '.matrix', 'credentials', 'matrix-api-key.json');
const configPath = join(demoHome, '.matrix', 'config.json');
if (!existsSync(credentialPath)) throw new Error(`Credential file was not written: ${credentialPath}`);
if (!existsSync(configPath)) throw new Error(`Config file was not written: ${configPath}`);
const writtenCredential = JSON.parse(readFileSync(credentialPath, 'utf8'));
if (writtenCredential.key !== config.key) throw new Error('Written credential key does not match input key');
if (writtenCredential.cloud !== config.cloud) throw new Error('Written credential cloud does not match input cloud');
if (writtenCredential.space !== config.space) throw new Error('Written credential space does not match input space');
console.log(JSON.stringify({
ok: true,
demo: 'hivecast-login',
cloud: config.cloud,
space: config.space,
keyValidated: true,
keyId: validation.keyId,
fingerprint: validation.fingerprint,
demoHome: keep ? demoHome : '(temporary, removed)',
credentialPath: keep ? credentialPath : '(temporary, removed)',
}, null, 2));
} finally {
if (!keep) rmSync(demoHome, { recursive: true, force: true });
}