docs: make SDK onboarding self-checking
This commit is contained in:
parent
4ab8dc7986
commit
dd1872e3de
1
.gitignore
vendored
1
.gitignore
vendored
@ -7,3 +7,4 @@ dist/
|
||||
.tsbuildinfo
|
||||
**/.tsbuildinfo
|
||||
*.tgz
|
||||
.tools/
|
||||
|
||||
82
README.md
82
README.md
@ -60,7 +60,8 @@ Prerequisites:
|
||||
|
||||
- Node.js 22+
|
||||
- pnpm 10+
|
||||
- `nats-server` on `PATH` for the no-account standalone broker demo
|
||||
- `nats-server` for the no-account standalone broker demo. The easiest path is
|
||||
`pnpm setup:nats`, which installs a repo-local server under `.tools/`.
|
||||
- Google Chrome/Chromium on `PATH`, or run `pnpm exec playwright install chromium`,
|
||||
for the browser proof in `prove:fresh-samples`
|
||||
|
||||
@ -68,9 +69,25 @@ Prerequisites:
|
||||
git clone git@github.com:matrix-sdk/matrix-sdk.git
|
||||
cd matrix-sdk
|
||||
pnpm install --frozen-lockfile
|
||||
pnpm run doctor
|
||||
pnpm prove
|
||||
```
|
||||
|
||||
If the standalone broker demo reports missing NATS:
|
||||
|
||||
```bash
|
||||
pnpm setup:nats
|
||||
pnpm run doctor:standalone
|
||||
```
|
||||
|
||||
If you need the HiveCast browser proof and `pnpm doctor:hivecast` reports
|
||||
missing Chromium:
|
||||
|
||||
```bash
|
||||
pnpm setup:browser
|
||||
pnpm run doctor:hivecast
|
||||
```
|
||||
|
||||
`pnpm prove` runs the standalone proof:
|
||||
|
||||
```text
|
||||
@ -161,9 +178,15 @@ belong in overlay packages, not in the SDK CLI core.
|
||||
|
||||
## Demos
|
||||
|
||||
Standalone SDK demo, no provider account or key required:
|
||||
### Path A: Local Broker, No HiveCast Account
|
||||
|
||||
Use this path when you only want to prove the SDK can run an actor over a real
|
||||
broker without a managed platform account.
|
||||
|
||||
```bash
|
||||
pnpm install --frozen-lockfile
|
||||
pnpm setup:nats
|
||||
pnpm run doctor:standalone
|
||||
pnpm demo:standalone
|
||||
```
|
||||
|
||||
@ -172,20 +195,38 @@ This builds the SDK and `examples/standalone-greeter`, starts the actor with
|
||||
and shuts it down. It does not use a provider account, machine link, managed
|
||||
host service, or local HTTP control server.
|
||||
|
||||
### Path B: HiveCast Broker, HiveCast API Key
|
||||
|
||||
Use this path when you want HiveCast to issue the broker credential. The SDK
|
||||
still runs the actor/page directly; HiveCast supplies NATS TCP/WebSocket URLs and
|
||||
short-lived actor credentials.
|
||||
|
||||
Create a HiveCast API key in the HiveCast web UI:
|
||||
|
||||
```text
|
||||
Settings -> API Keys -> Create Key
|
||||
Purpose: Machine install
|
||||
Required scopes: host:init host:credentials:issue profile:install
|
||||
```
|
||||
|
||||
Then run:
|
||||
|
||||
```bash
|
||||
export MATRIX_CLOUD=https://<your-hivecast-cloud>
|
||||
export MATRIX_SPACE=<your-space-root>
|
||||
export MATRIX_API_KEY=<your-hivecast-api-key>
|
||||
|
||||
pnpm install --frozen-lockfile
|
||||
pnpm setup:browser
|
||||
pnpm run doctor:hivecast
|
||||
pnpm prove:fresh-samples
|
||||
```
|
||||
|
||||
Provider API-key login/config work has been moved out of the active SDK demo set
|
||||
and preserved under `archive/sdk-provider-overlay-candidates/`. It belongs to a
|
||||
provider overlay that can write the same broker config shape consumed by the SDK
|
||||
CLI.
|
||||
|
||||
HiveCast-managed SDK demos, API key required:
|
||||
|
||||
```bash
|
||||
MATRIX_CLOUD=https://<your-hivecast-cloud> \
|
||||
MATRIX_SPACE=<your-space-root> \
|
||||
MATRIX_API_KEY=<your-hivecast-api-key> \
|
||||
pnpm prove:fresh-samples
|
||||
```
|
||||
|
||||
That proof builds and runs two fresh package-author demos:
|
||||
|
||||
- `examples/fresh-server-actor` exchanges the API key for an actor-scoped
|
||||
@ -199,6 +240,25 @@ That proof builds and runs two fresh package-author demos:
|
||||
The API key stays server-side for the web demo. The browser receives only the
|
||||
short-lived actor JWT/seed returned by the HiveCast credential exchange.
|
||||
|
||||
### Path C: Custom Broker
|
||||
|
||||
Use this path when you already operate a broker and want to pass the connection
|
||||
explicitly.
|
||||
|
||||
```bash
|
||||
matrix package run . \
|
||||
--nats-url nats://<host>:4222 \
|
||||
--root <subject-root> \
|
||||
--jwt '<optional-user-jwt>' \
|
||||
--seed '<optional-user-seed>' \
|
||||
--check-op ping
|
||||
```
|
||||
|
||||
The SDK CLI also reads `MATRIX_NATS_URL`, `MATRIX_ROOT`/`MATRIX_SPACE`,
|
||||
`MATRIX_NATS_JWT`, and `MATRIX_NATS_SEED`, plus config files under `--home`,
|
||||
`./.matrix`, and `~/.matrix`. The SDK CLI does not create custom-broker
|
||||
accounts. It consumes broker access you already have.
|
||||
|
||||
## Repository Layout
|
||||
|
||||
```text
|
||||
|
||||
@ -16,7 +16,8 @@ export MATRIX_CLOUD=https://<your-hivecast-cloud>
|
||||
export MATRIX_SPACE=<your-space-root>
|
||||
export MATRIX_API_KEY=<your-hivecast-api-key>
|
||||
|
||||
pnpm install
|
||||
pnpm install --frozen-lockfile
|
||||
pnpm run doctor
|
||||
pnpm --filter matrix-sdk-example-fresh-server-actor build
|
||||
pnpm --filter matrix-sdk-example-fresh-server-actor check
|
||||
```
|
||||
|
||||
@ -21,7 +21,9 @@ export MATRIX_CLOUD=https://<your-hivecast-cloud>
|
||||
export MATRIX_SPACE=<your-space-root>
|
||||
export MATRIX_API_KEY=<your-hivecast-api-key>
|
||||
|
||||
pnpm install
|
||||
pnpm install --frozen-lockfile
|
||||
pnpm setup:browser
|
||||
pnpm run doctor:hivecast
|
||||
pnpm --filter matrix-sdk-example-fresh-web-page dev
|
||||
```
|
||||
|
||||
|
||||
@ -6,6 +6,9 @@ and without committed credentials.
|
||||
Run from the repository root:
|
||||
|
||||
```bash
|
||||
pnpm install --frozen-lockfile
|
||||
pnpm setup:nats
|
||||
pnpm run doctor:standalone
|
||||
pnpm demo:standalone
|
||||
```
|
||||
|
||||
|
||||
@ -2,6 +2,7 @@ import { spawn, spawnSync } from 'node:child_process';
|
||||
import { createServer, createConnection } from 'node:net';
|
||||
import { dirname, resolve } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { resolveNatsServer } from '../../scripts/lib/nats-server-tool.mjs';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const repoRoot = resolve(__dirname, '..', '..');
|
||||
@ -82,7 +83,15 @@ try {
|
||||
run('pnpm', ['run', 'build']);
|
||||
run('pnpm', ['--filter', 'matrix-sdk-example-standalone-greeter', 'run', 'build']);
|
||||
|
||||
natsServer = spawn('nats-server', ['-p', String(port), '-a', '127.0.0.1'], {
|
||||
const nats = resolveNatsServer(repoRoot);
|
||||
if (!nats) {
|
||||
throw new Error([
|
||||
'nats-server is required for pnpm demo:standalone.',
|
||||
'Run pnpm setup:nats, install NATS Server on PATH, or set MATRIX_NATS_SERVER=/path/to/nats-server.',
|
||||
].join(' '));
|
||||
}
|
||||
|
||||
natsServer = spawn(nats.path, ['-p', String(port), '-a', '127.0.0.1'], {
|
||||
cwd: repoRoot,
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
env: process.env,
|
||||
@ -117,6 +126,10 @@ try {
|
||||
mount: body.mount,
|
||||
root: body.root,
|
||||
natsUrl: body.natsUrl,
|
||||
natsServer: {
|
||||
source: nats.source,
|
||||
path: nats.path,
|
||||
},
|
||||
authMode: body.authMode,
|
||||
check: body.check,
|
||||
}, null, 2));
|
||||
|
||||
@ -10,8 +10,13 @@
|
||||
"test": "pnpm -r --sort --if-present run test",
|
||||
"type-check": "pnpm -r --sort --if-present run type-check",
|
||||
"pack:all": "pnpm --filter './packages/*' -r exec pnpm pack",
|
||||
"doctor": "node scripts/doctor.mjs",
|
||||
"doctor:standalone": "node scripts/doctor.mjs --standalone",
|
||||
"doctor:hivecast": "node scripts/doctor.mjs --hivecast",
|
||||
"setup:nats": "node scripts/setup-nats-server.mjs",
|
||||
"setup:browser": "pnpm exec playwright install chromium",
|
||||
"demo:standalone": "node examples/standalone-greeter/run-demo.mjs",
|
||||
"prove:cli": "pnpm --filter @open-matrix/cli build && pnpm --filter @open-matrix/cli test && pnpm run demo:standalone",
|
||||
"prove:cli": "pnpm --filter @open-matrix/cli... build && pnpm --filter @open-matrix/cli test && pnpm run demo:standalone",
|
||||
"prove:external-consumer": "node scripts/prove-external-consumer.mjs",
|
||||
"prove:fresh-samples": "node scripts/prove-fresh-samples.mjs",
|
||||
"prove": "pnpm run clean && pnpm run build && pnpm run test && pnpm run type-check && pnpm run pack:all"
|
||||
|
||||
119
scripts/doctor.mjs
Normal file
119
scripts/doctor.mjs
Normal file
@ -0,0 +1,119 @@
|
||||
#!/usr/bin/env node
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { existsSync } from 'node:fs';
|
||||
import { resolveNatsServer, readNatsServerVersion } from './lib/nats-server-tool.mjs';
|
||||
|
||||
const json = process.argv.includes('--json');
|
||||
const mode = process.argv.includes('--standalone')
|
||||
? 'standalone'
|
||||
: process.argv.includes('--hivecast')
|
||||
? 'hivecast'
|
||||
: process.argv.includes('--all')
|
||||
? 'all'
|
||||
: 'base';
|
||||
|
||||
function check(name, ok, details = {}) {
|
||||
return { name, ok, ...details };
|
||||
}
|
||||
|
||||
function run(command, args) {
|
||||
const result = spawnSync(command, args, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] });
|
||||
if (result.error) return { ok: false, error: result.error.message };
|
||||
if (result.status !== 0) return { ok: false, error: `${command} exited ${result.status}`, stderr: result.stderr.trim() };
|
||||
return { ok: true, stdout: result.stdout.trim(), stderr: result.stderr.trim() };
|
||||
}
|
||||
|
||||
function major(version) {
|
||||
return Number(String(version).split('.')[0] ?? 0);
|
||||
}
|
||||
|
||||
function findBrowser() {
|
||||
if (process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE && existsSync(process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE)) {
|
||||
return { path: process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE, source: 'PLAYWRIGHT_CHROMIUM_EXECUTABLE' };
|
||||
}
|
||||
for (const path of ['/usr/bin/google-chrome', '/usr/bin/google-chrome-stable', '/usr/bin/chromium', '/usr/bin/chromium-browser']) {
|
||||
if (existsSync(path)) return { path, source: 'system' };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const checks = [];
|
||||
|
||||
const nodeVersion = process.versions.node;
|
||||
checks.push(check('node', major(nodeVersion) >= 22, {
|
||||
version: nodeVersion,
|
||||
required: '22+',
|
||||
fix: 'Install Node.js 22 or newer.',
|
||||
}));
|
||||
|
||||
const pnpm = run('pnpm', ['--version']);
|
||||
checks.push(check('pnpm', pnpm.ok && major(pnpm.stdout) >= 10, {
|
||||
version: pnpm.ok ? pnpm.stdout : null,
|
||||
required: '10+',
|
||||
fix: 'Install pnpm 10: corepack enable && corepack prepare pnpm@10.29.3 --activate',
|
||||
error: pnpm.ok ? undefined : pnpm.error,
|
||||
}));
|
||||
|
||||
let nats = null;
|
||||
try {
|
||||
nats = resolveNatsServer();
|
||||
checks.push(check('nats-server', Boolean(nats), {
|
||||
path: nats?.path ?? null,
|
||||
source: nats?.source ?? null,
|
||||
version: nats ? readNatsServerVersion(nats.path) : null,
|
||||
requiredFor: 'pnpm demo:standalone',
|
||||
fix: 'Run pnpm setup:nats, or install NATS Server and put nats-server on PATH.',
|
||||
}));
|
||||
} catch (error) {
|
||||
checks.push(check('nats-server', false, {
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
requiredFor: 'pnpm demo:standalone',
|
||||
fix: 'Run pnpm setup:nats, or set MATRIX_NATS_SERVER to the nats-server binary.',
|
||||
}));
|
||||
}
|
||||
|
||||
const browser = findBrowser();
|
||||
checks.push(check('chromium', Boolean(browser), {
|
||||
path: browser?.path ?? null,
|
||||
source: browser?.source ?? null,
|
||||
requiredFor: 'live browser proof in pnpm prove:fresh-samples',
|
||||
fix: 'Run pnpm setup:browser, or set PLAYWRIGHT_CHROMIUM_EXECUTABLE to Chrome/Chromium.',
|
||||
}));
|
||||
|
||||
const hivecastEnv = {
|
||||
MATRIX_CLOUD: Boolean(process.env.MATRIX_CLOUD),
|
||||
MATRIX_SPACE: Boolean(process.env.MATRIX_SPACE),
|
||||
MATRIX_API_KEY: Boolean(process.env.MATRIX_API_KEY),
|
||||
};
|
||||
checks.push(check('hivecast-env', Object.values(hivecastEnv).every(Boolean), {
|
||||
present: hivecastEnv,
|
||||
requiredFor: 'live HiveCast server/browser samples in pnpm prove:fresh-samples',
|
||||
fix: 'Create a HiveCast API key, then export MATRIX_CLOUD, MATRIX_SPACE, and MATRIX_API_KEY.',
|
||||
}));
|
||||
|
||||
const requiredByMode = {
|
||||
base: ['node', 'pnpm'],
|
||||
standalone: ['node', 'pnpm', 'nats-server'],
|
||||
hivecast: ['node', 'pnpm', 'chromium', 'hivecast-env'],
|
||||
all: ['node', 'pnpm', 'nats-server', 'chromium', 'hivecast-env'],
|
||||
};
|
||||
const requiredFailures = checks.filter((item) => requiredByMode[mode].includes(item.name) && !item.ok);
|
||||
const proof = {
|
||||
ok: requiredFailures.length === 0,
|
||||
mode,
|
||||
checks,
|
||||
};
|
||||
|
||||
if (json) {
|
||||
console.log(JSON.stringify(proof, null, 2));
|
||||
} else {
|
||||
console.log(`Matrix SDK doctor mode: ${mode}`);
|
||||
for (const item of checks) {
|
||||
const required = requiredByMode[mode].includes(item.name);
|
||||
const mark = item.ok ? 'ok' : required ? 'missing' : 'optional-missing';
|
||||
console.log(`${mark} ${item.name}${item.version ? ` (${item.version})` : ''}${item.path ? ` at ${item.path}` : ''}`);
|
||||
if (!item.ok) console.log(` fix: ${item.fix}`);
|
||||
}
|
||||
}
|
||||
|
||||
process.exit(proof.ok ? 0 : 1);
|
||||
170
scripts/lib/nats-server-tool.mjs
Normal file
170
scripts/lib/nats-server-tool.mjs
Normal file
@ -0,0 +1,170 @@
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { createWriteStream, existsSync, mkdirSync, readdirSync, rmSync, statSync } from 'node:fs';
|
||||
import { chmod, copyFile } from 'node:fs/promises';
|
||||
import { get } from 'node:https';
|
||||
import { dirname, join, resolve } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
export const NATS_SERVER_VERSION = '2.14.1';
|
||||
|
||||
const thisFile = fileURLToPath(import.meta.url);
|
||||
export const repoRoot = resolve(dirname(thisFile), '..', '..');
|
||||
|
||||
function platformName() {
|
||||
if (process.platform === 'linux') return 'linux';
|
||||
if (process.platform === 'darwin') return 'darwin';
|
||||
if (process.platform === 'win32') return 'windows';
|
||||
throw new Error(`Unsupported platform for managed nats-server install: ${process.platform}`);
|
||||
}
|
||||
|
||||
function archName() {
|
||||
if (process.arch === 'x64') return 'amd64';
|
||||
if (process.arch === 'arm64') return 'arm64';
|
||||
throw new Error(`Unsupported architecture for managed nats-server install: ${process.arch}`);
|
||||
}
|
||||
|
||||
function executableName() {
|
||||
return process.platform === 'win32' ? 'nats-server.exe' : 'nats-server';
|
||||
}
|
||||
|
||||
export function managedNatsServerPath(root = repoRoot) {
|
||||
return join(root, '.tools', 'nats-server', `v${NATS_SERVER_VERSION}`, `${platformName()}-${archName()}`, 'bin', executableName());
|
||||
}
|
||||
|
||||
function pathEntries() {
|
||||
return (process.env.PATH ?? '').split(process.platform === 'win32' ? ';' : ':').filter(Boolean);
|
||||
}
|
||||
|
||||
function executableExists(path) {
|
||||
try {
|
||||
return existsSync(path) && statSync(path).isFile();
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function findOnPath(name) {
|
||||
for (const entry of pathEntries()) {
|
||||
const candidate = join(entry, name);
|
||||
if (executableExists(candidate)) return candidate;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function resolveNatsServer(root = repoRoot) {
|
||||
const explicit = process.env.MATRIX_NATS_SERVER;
|
||||
if (explicit) {
|
||||
const resolved = resolve(explicit);
|
||||
if (!executableExists(resolved)) {
|
||||
throw new Error(`MATRIX_NATS_SERVER points to a missing file: ${resolved}`);
|
||||
}
|
||||
return { path: resolved, source: 'MATRIX_NATS_SERVER' };
|
||||
}
|
||||
|
||||
const managed = managedNatsServerPath(root);
|
||||
if (executableExists(managed)) return { path: managed, source: 'repo-managed' };
|
||||
|
||||
const onPath = findOnPath(executableName()) ?? findOnPath('nats-server');
|
||||
if (onPath) return { path: onPath, source: 'PATH' };
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function natsServerInstallUrl(version = NATS_SERVER_VERSION) {
|
||||
const format = process.platform === 'win32' ? 'zip' : 'tar.gz';
|
||||
const asset = `nats-server-v${version}-${platformName()}-${archName()}.${format}`;
|
||||
return {
|
||||
asset,
|
||||
url: `https://github.com/nats-io/nats-server/releases/download/v${version}/${asset}`,
|
||||
format,
|
||||
};
|
||||
}
|
||||
|
||||
function download(url, destination) {
|
||||
return new Promise((resolveDownload, rejectDownload) => {
|
||||
const request = get(url, (response) => {
|
||||
if ([301, 302, 303, 307, 308].includes(response.statusCode ?? 0) && response.headers.location) {
|
||||
response.resume();
|
||||
download(response.headers.location, destination).then(resolveDownload, rejectDownload);
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.statusCode !== 200) {
|
||||
response.resume();
|
||||
rejectDownload(new Error(`Download failed with HTTP ${response.statusCode}: ${url}`));
|
||||
return;
|
||||
}
|
||||
|
||||
const file = createWriteStream(destination);
|
||||
response.pipe(file);
|
||||
file.on('finish', () => {
|
||||
file.close(resolveDownload);
|
||||
});
|
||||
file.on('error', rejectDownload);
|
||||
});
|
||||
request.on('error', rejectDownload);
|
||||
});
|
||||
}
|
||||
|
||||
function walkFiles(dir) {
|
||||
const out = [];
|
||||
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
||||
const path = join(dir, entry.name);
|
||||
if (entry.isDirectory()) out.push(...walkFiles(path));
|
||||
else out.push(path);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function extractArchive(archive, destination, format) {
|
||||
mkdirSync(destination, { recursive: true });
|
||||
if (format === 'tar.gz') {
|
||||
const result = spawnSync('tar', ['-xzf', archive, '-C', destination], { stdio: 'inherit' });
|
||||
if (result.error) throw result.error;
|
||||
if (result.status !== 0) throw new Error(`tar extraction failed with exit ${result.status}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const script = `Expand-Archive -LiteralPath ${JSON.stringify(archive)} -DestinationPath ${JSON.stringify(destination)} -Force`;
|
||||
const result = spawnSync('powershell.exe', ['-NoProfile', '-Command', script], { stdio: 'inherit' });
|
||||
if (result.error) throw result.error;
|
||||
if (result.status !== 0) throw new Error(`zip extraction failed with exit ${result.status}`);
|
||||
}
|
||||
|
||||
export async function installManagedNatsServer(root = repoRoot) {
|
||||
const existing = resolveNatsServer(root);
|
||||
if (existing?.source === 'repo-managed') {
|
||||
return { ...existing, installed: false };
|
||||
}
|
||||
|
||||
const target = managedNatsServerPath(root);
|
||||
const installRoot = dirname(dirname(target));
|
||||
const tmpRoot = join(root, '.tools', 'tmp', `nats-server-v${NATS_SERVER_VERSION}-${Date.now()}`);
|
||||
const { asset, url, format } = natsServerInstallUrl();
|
||||
const archive = join(tmpRoot, asset);
|
||||
const extractDir = join(tmpRoot, 'extract');
|
||||
|
||||
rmSync(installRoot, { recursive: true, force: true });
|
||||
mkdirSync(tmpRoot, { recursive: true });
|
||||
|
||||
try {
|
||||
await download(url, archive);
|
||||
extractArchive(archive, extractDir, format);
|
||||
const binary = walkFiles(extractDir).find((path) => path.endsWith(executableName()) || path.endsWith('/nats-server'));
|
||||
if (!binary) throw new Error(`Downloaded ${asset} did not contain ${executableName()}`);
|
||||
|
||||
mkdirSync(dirname(target), { recursive: true });
|
||||
await copyFile(binary, target);
|
||||
if (process.platform !== 'win32') await chmod(target, 0o755);
|
||||
return { path: target, source: 'repo-managed', installed: true, url };
|
||||
} finally {
|
||||
rmSync(tmpRoot, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
|
||||
export function readNatsServerVersion(binaryPath) {
|
||||
const result = spawnSync(binaryPath, ['--version'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] });
|
||||
if (result.error) throw result.error;
|
||||
if (result.status !== 0) throw new Error(`${binaryPath} --version failed with exit ${result.status}`);
|
||||
return `${result.stdout}${result.stderr}`.trim();
|
||||
}
|
||||
33
scripts/setup-nats-server.mjs
Normal file
33
scripts/setup-nats-server.mjs
Normal file
@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env node
|
||||
import { installManagedNatsServer, readNatsServerVersion } from './lib/nats-server-tool.mjs';
|
||||
|
||||
const json = process.argv.includes('--json');
|
||||
|
||||
try {
|
||||
const result = await installManagedNatsServer();
|
||||
const version = readNatsServerVersion(result.path);
|
||||
const proof = {
|
||||
ok: true,
|
||||
tool: 'nats-server',
|
||||
installed: result.installed,
|
||||
source: result.source,
|
||||
path: result.path,
|
||||
version,
|
||||
url: result.url,
|
||||
};
|
||||
if (json) {
|
||||
console.log(JSON.stringify(proof, null, 2));
|
||||
} else {
|
||||
console.log(`nats-server ready: ${version}`);
|
||||
console.log(`path: ${result.path}`);
|
||||
console.log(`source: ${result.source}${result.installed ? ' (installed now)' : ''}`);
|
||||
}
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
if (json) {
|
||||
console.log(JSON.stringify({ ok: false, tool: 'nats-server', error: message }, null, 2));
|
||||
} else {
|
||||
console.error(`nats-server setup failed: ${message}`);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user