auto-builder-sdk
Version:
SDK for building Auto Builder workflow plugins
144 lines (137 loc) • 4.92 kB
JavaScript
#!/usr/bin/env node
// auto-builder-sdk/src/bin/cli.ts
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import fs from 'node:fs';
import inquirer from 'inquirer';
import chalk from 'chalk';
const [, , cmd, pluginName] = process.argv;
const usage = () => {
console.log('\nUsage:');
console.log(' npx auto-builder-sdk init <my-plugin>\n');
};
if (cmd !== 'init' || !pluginName) {
usage();
process.exit(cmd ? 1 : 0);
}
// Resolve SDK version by walking up directories until we find the nearest
// package.json (works both from `src/` and the compiled `dist/` layout).
const findPkgJson = (start) => {
let dir = start;
// eslint-disable-next-line no-constant-condition
while (true) {
const candidate = join(dir, 'package.json');
if (fs.existsSync(candidate))
return candidate;
const parent = dirname(dir);
if (parent === dir)
throw new Error('package.json not found');
dir = parent;
}
};
const pkgPath = findPkgJson(dirname(fileURLToPath(import.meta.url)));
const { version: sdkVersion } = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
(async () => {
const dest = join(process.cwd(), pluginName);
if (fs.existsSync(dest)) {
console.error(chalk.red(`Directory ${pluginName} already exists`));
process.exit(1);
}
let description = process.env.PLUGIN_DESC ?? '';
if (process.stdin.isTTY && !description) {
const answers = await inquirer.prompt([
{ type: 'input', name: 'description', message: 'Plugin description:', default: '' },
]);
description = answers.description;
}
fs.mkdirSync(dest, { recursive: true });
fs.writeFileSync(join(dest, 'package.json'), JSON.stringify({
name: pluginName,
version: '0.0.1',
description,
type: 'module',
main: 'dist/index.js',
nodes: ['HelloNode'],
engines: {
'auto-builder': '^1.0.0',
node: '>=18',
},
scripts: {
build: 'tsc',
test: 'vitest',
verify: 'vitest run --coverage',
prepare: 'npm run build',
prepublishOnly: 'npm run verify',
dev: 'nodemon --watch src --ext ts,js,json --exec "npm run build && npm run deploy-plugin"',
'deploy-plugin': `mkdir -p ../../auto-builder/plugins/${pluginName} && cp -R ./dist ../../auto-builder/plugins/${pluginName}/ && cp package.json ../../auto-builder/plugins/${pluginName}/`,
},
devDependencies: {
typescript: '^5.8.3',
vitest: '^1.6.1',
'@vitest/coverage-v8': '^1.6.1',
'auto-builder-sdk': `^${sdkVersion}`,
},
'auto-builder-plugin': true,
}, null, 2));
// --- Vitest configuration with coverage provider ------------------
fs.writeFileSync(join(dest, 'vitest.config.ts'), `import { defineConfig } from 'vitest/config';\n\nexport default defineConfig({\n test: {\n coverage: { provider: 'v8' },\n },\n});\n`);
// --- TypeScript config -------------------------------------------
fs.writeFileSync(join(dest, 'tsconfig.json'), `{
"compilerOptions": {
"rootDir": "src",
"outDir": "dist",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"target": "ES2022",
"strict": true,
"declaration": true,
"esModuleInterop": true,
"skipLibCheck": true
},
"include": ["src"]
}
`);
const srcDir = join(dest, 'src');
fs.mkdirSync(srcDir);
const sample = `import { BaseNodeExecutor } from 'auto-builder-sdk';
export class HelloNode extends BaseNodeExecutor {
static type = 'hello.world';
readonly nodeType = 'hello.world';
async execute(node, inputData, context) {
return [{ json: { message: 'Hello from ${pluginName}!' }, binary: {} }];
}
}
export default {
name: '${pluginName}',
version: '0.0.1',
main: './dist/index.js',
nodes: ['HelloNode'],
engines: { 'auto-builder': '^1.0.0' },
};
`;
fs.writeFileSync(join(srcDir, 'index.ts'), sample);
// --- Sample unit test --------------------------------------------
const testsDir = join(dest, 'tests');
fs.mkdirSync(testsDir);
fs.writeFileSync(join(testsDir, 'hello-node.test.ts'), `import { describe, it, expect, vi } from 'vitest';
// Minimal stub for SDK so the test runs without full Auto-Builder.
vi.mock('auto-builder-sdk', () => {
class StubBase {
async execute() { return []; }
}
return {
BaseNodeExecutor: StubBase,
definePlugin: (m: any) => m,
makeStubContext: () => ({}) as any,
makeStubNode: (t: string) => ({ type: t }) as any,
};
});
import { HelloNode } from '../src/index';
describe('HelloNode', () => {
it('class is defined', () => {
expect(HelloNode).toBeDefined();
});
});
`);
console.log(chalk.green(`\n✔ Plugin ${pluginName} scaffolded!`));
})();