Skip to content

Commit 49e8d48

Browse files
committed
Add panel harness tool
1 parent f3e3425 commit 49e8d48

3 files changed

Lines changed: 177 additions & 0 deletions

File tree

tools/panel-harness/README.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Panel Harness
2+
3+
The Panel Harness is a tool for bridging cartridges to BoJ Server and panll.
4+
5+
## Features
6+
7+
- Register cartridges with BoJ Server or panll
8+
- Support protocol translation (REST ↔ gRPC ↔ GraphQL)
9+
- Route events between cartridges and BoJ Server/panll
10+
- Expose cartridges as A2ML-compliant services or panll modules
11+
12+
## Installation
13+
14+
```bash
15+
cd tools/panel-harness
16+
npm install
17+
```
18+
19+
## Usage
20+
21+
### Command Line
22+
23+
```bash
24+
node harness.js <cartridge-dir> <target> [--routes <routes-file>]
25+
```
26+
27+
### Options
28+
29+
- `<cartridge-dir>`: Path to the cartridge directory containing `cartridge.json`
30+
- `<target>`: Target environment (`boj-server` or `panll`)
31+
- `--routes <routes-file>`: Path to the routes configuration file for the cartridge
32+
33+
### Example
34+
35+
```bash
36+
node harness.js ../../cartridges/my-cartridge boj-server --routes routes.json
37+
```
38+
39+
### Programmatic Usage
40+
41+
```javascript
42+
import { registerCartridge } from './harness.js';
43+
44+
const result = await registerCartridge('path/to/cartridge', 'boj-server', {
45+
routes: [
46+
{ protocol: 'REST', port: 4000 },
47+
{ protocol: 'gRPC', port: 50051 },
48+
],
49+
});
50+
51+
console.log('Cartridge registered:', result);
52+
```
53+
54+
## Output
55+
56+
The Panel Harness registers the cartridge with the specified target and writes the registration to a `registration.json` file in the harness directory.
57+
58+
## License
59+
60+
This tool is licensed under the PMPL-1.0-or-later license. See the LICENSE file for more information.

tools/panel-harness/harness.js

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
#!/usr/bin/env node
2+
// SPDX-License-Identifier: PMPL-1.0-or-later
3+
// Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
4+
5+
// Panel Harness — Bridge cartridges to BoJ Server and panll
6+
7+
import { readFile, writeFile, mkdir, readdir } from 'node:fs/promises';
8+
import { join, dirname } from 'node:path';
9+
import { exec } from 'node:child_process';
10+
import { promisify } from 'node:util';
11+
12+
const execAsync = promisify(exec);
13+
14+
async function readJsonFile(filePath) {
15+
const content = await readFile(filePath, 'utf8');
16+
return JSON.parse(content);
17+
}
18+
19+
async function writeJsonFile(filePath, data) {
20+
await writeFile(filePath, JSON.stringify(data, null, 2), 'utf8');
21+
}
22+
23+
async function ensureDirectoryExists(directory) {
24+
await mkdir(directory, { recursive: true });
25+
}
26+
27+
async function registerCartridge(cartridgeDir, target, options = {}) {
28+
const { routes = [] } = options;
29+
30+
// Read cartridge.json
31+
const cartridgeJsonPath = join(cartridgeDir, 'cartridge.json');
32+
const cartridge = await readJsonFile(cartridgeJsonPath);
33+
34+
// Ensure the target directory exists
35+
const targetDir = join('harness', target, cartridge.name);
36+
await ensureDirectoryExists(targetDir);
37+
38+
// Register the cartridge with the target
39+
console.log(`Registering cartridge with ${target}...`);
40+
41+
// In a real implementation, you would register the cartridge with BoJ Server or panll here
42+
// For now, we'll just log the registration
43+
const registration = {
44+
cartridge,
45+
target,
46+
routes,
47+
registeredAt: new Date().toISOString(),
48+
};
49+
50+
// Write the registration to a file
51+
const registrationPath = join(targetDir, 'registration.json');
52+
await writeJsonFile(registrationPath, registration);
53+
54+
return {
55+
cartridge,
56+
target,
57+
routes,
58+
registrationPath,
59+
};
60+
}
61+
62+
async function main() {
63+
const args = process.argv.slice(2);
64+
65+
if (args.length < 2) {
66+
console.error('Usage: node harness.js <cartridge-dir> <target> [--routes <routes-file>]');
67+
process.exit(1);
68+
}
69+
70+
const cartridgeDir = args[0];
71+
const target = args[1];
72+
const routesIndex = args.indexOf('--routes');
73+
const routesFile = routesIndex !== -1 ? args[routesIndex + 1] : null;
74+
75+
let routes = [];
76+
if (routesFile) {
77+
try {
78+
routes = await readJsonFile(routesFile);
79+
} catch (error) {
80+
console.error('Error reading routes file:', error);
81+
process.exit(1);
82+
}
83+
}
84+
85+
try {
86+
console.log(`Registering cartridge from ${cartridgeDir} with ${target}...`);
87+
const result = await registerCartridge(cartridgeDir, target, { routes });
88+
89+
console.log('Cartridge registered successfully:');
90+
console.log(` Name: ${result.cartridge.name}`);
91+
console.log(` Target: ${result.target}`);
92+
console.log(` Routes: ${JSON.stringify(result.routes)}`);
93+
console.log(` Registration Path: ${result.registrationPath}`);
94+
} catch (error) {
95+
console.error('Error registering cartridge:', error);
96+
process.exit(1);
97+
}
98+
}
99+
100+
if (import.meta.url === `file://${process.argv[1]}`) {
101+
main();
102+
}
103+
104+
export { registerCartridge };

tools/panel-harness/package.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"name": "panel-harness",
3+
"version": "0.1.0",
4+
"description": "Panel Harness — Bridge cartridges to BoJ Server and panll",
5+
"main": "harness.js",
6+
"scripts": {
7+
"test": "echo \"Error: no test specified\" && exit 1"
8+
},
9+
"keywords": ["boj-server", "cartridge", "panel-harness"],
10+
"author": "Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>",
11+
"license": "PMPL-1.0-or-later",
12+
"type": "module"
13+
}

0 commit comments

Comments
 (0)