feat(cli): extract Obelisk runtime into npm package
Add @obelisk-apps/cli with the existing build, search, query, and attune contract plus official skill installation. Separate the docs-only skill artifact, bootstrap installer, release layout, cross-platform CI, and package-level regression coverage.
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { execFileSync, spawnSync } from 'node:child_process';
|
||||
import { chmodSync, mkdirSync, mkdtempSync, readFileSync, writeFileSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { delimiter, join } from 'node:path';
|
||||
|
||||
import { repoRoot, runCli } from './cli-test-helpers.mjs';
|
||||
|
||||
const npmCommand = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
||||
const cliPackage = JSON.parse(readFileSync(join(repoRoot, 'packages', 'cli', 'package.json'), 'utf8'));
|
||||
|
||||
test('the packaged obelisk command preserves the runtime query envelope', () => {
|
||||
const home = mkdtempSync(join(tmpdir(), 'obelisk-cli-package-'));
|
||||
const query = join(home, 'query.mjs');
|
||||
writeFileSync(query, 'return { answer: 42 };');
|
||||
|
||||
const result = runCli(['--query', query], { home });
|
||||
|
||||
assert.equal(result.status, 0, result.stderr || result.stdout);
|
||||
assert.equal(result.stderr, '');
|
||||
assert.equal(result.stdout, '{\n "answer": 42\n}\n');
|
||||
});
|
||||
|
||||
test('obelisk --version reports the installed CLI package version', () => {
|
||||
const result = runCli(['--version']);
|
||||
|
||||
assert.equal(result.status, 0, result.stderr || result.stdout);
|
||||
assert.equal(result.stdout, `${cliPackage.version}\n`);
|
||||
assert.equal(result.stderr, '');
|
||||
});
|
||||
|
||||
test('obelisk install delegates official skill installation to the skills CLI', () => {
|
||||
const home = mkdtempSync(join(tmpdir(), 'obelisk-cli-install-'));
|
||||
const fakeBin = join(home, 'bin');
|
||||
const capture = join(home, 'args.json');
|
||||
const captureScript = join(home, 'capture.mjs');
|
||||
mkdirSync(fakeBin, { recursive: true });
|
||||
writeFileSync(captureScript, `import { writeFileSync } from 'node:fs';\nwriteFileSync(process.env.OBELISK_TEST_CAPTURE, JSON.stringify(process.argv.slice(2)));\n`);
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
writeFileSync(
|
||||
join(fakeBin, 'npx.cmd'),
|
||||
`@echo off\r\n"${process.execPath}" "${captureScript}" %*\r\n`,
|
||||
);
|
||||
} else {
|
||||
const fakeNpx = join(fakeBin, 'npx');
|
||||
writeFileSync(fakeNpx, `#!/bin/sh\nexec "${process.execPath}" "${captureScript}" "$@"\n`);
|
||||
chmodSync(fakeNpx, 0o755);
|
||||
}
|
||||
|
||||
const result = runCli(['install', '--global', '--agent', 'codex'], {
|
||||
home,
|
||||
env: {
|
||||
PATH: `${fakeBin}${delimiter}${process.env.PATH || ''}`,
|
||||
OBELISK_TEST_CAPTURE: capture,
|
||||
},
|
||||
});
|
||||
|
||||
assert.equal(result.status, 0, result.stderr || result.stdout);
|
||||
assert.deepEqual(JSON.parse(readFileSync(capture, 'utf8')), [
|
||||
'--yes',
|
||||
'skills',
|
||||
'add',
|
||||
'tommy0103/obelisk-skill',
|
||||
'--global',
|
||||
'--agent',
|
||||
'codex',
|
||||
]);
|
||||
});
|
||||
|
||||
test('npm pack installs one platform-neutral CLI with its schema resource', () => {
|
||||
const root = mkdtempSync(join(tmpdir(), 'obelisk-cli-pack-'));
|
||||
const packDir = join(root, 'pack');
|
||||
const prefix = join(root, 'prefix');
|
||||
const npmCache = join(root, 'npm-cache');
|
||||
const npmEnv = { ...process.env, npm_config_cache: npmCache };
|
||||
mkdirSync(packDir, { recursive: true });
|
||||
|
||||
const packed = JSON.parse(execFileSync(
|
||||
npmCommand,
|
||||
[
|
||||
'pack',
|
||||
'--workspace',
|
||||
'@obelisk-apps/cli',
|
||||
'--pack-destination',
|
||||
packDir,
|
||||
'--json',
|
||||
'--ignore-scripts',
|
||||
],
|
||||
{
|
||||
cwd: repoRoot,
|
||||
env: npmEnv,
|
||||
encoding: 'utf8',
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
},
|
||||
));
|
||||
const metadata = packed[0];
|
||||
const paths = metadata.files.map(file => file.path);
|
||||
assert.ok(paths.includes('dist/cli/src/obelisk.js'));
|
||||
assert.ok(paths.includes('dist/core/src/schema.sql'));
|
||||
assert.equal(paths.some(path => path.endsWith('.ts')), false);
|
||||
|
||||
const tarball = join(packDir, metadata.filename);
|
||||
execFileSync(
|
||||
npmCommand,
|
||||
['install', '--global', '--prefix', prefix, tarball, '--ignore-scripts'],
|
||||
{ cwd: repoRoot, env: npmEnv, encoding: 'utf8', stdio: 'pipe' },
|
||||
);
|
||||
|
||||
const installedBin = process.platform === 'win32'
|
||||
? join(prefix, 'obelisk.cmd')
|
||||
: join(prefix, 'bin', 'obelisk');
|
||||
const result = spawnSync(installedBin, ['--version'], {
|
||||
cwd: repoRoot,
|
||||
encoding: 'utf8',
|
||||
shell: process.platform === 'win32',
|
||||
});
|
||||
|
||||
assert.equal(result.status, 0, result.stderr || result.stdout);
|
||||
assert.equal(result.stdout.trim(), cliPackage.version);
|
||||
});
|
||||
Reference in New Issue
Block a user