2026-06-07 23:45:51 +00:00
|
|
|
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)
|
2026-06-08 00:17:38 +00:00
|
|
|
: mkdtempSync(join(tmpdir(), 'hivecast-sdk-login.'));
|
2026-06-07 23:45:51 +00:00
|
|
|
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 });
|
|
|
|
|
}
|