@briine/create-bot 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,31 @@
1
+ # @briine/create-bot
2
+
3
+ Scaffold a new Briine bot project.
4
+
5
+ ## Usage
6
+
7
+ ```bash
8
+ npm create @briine/bot@latest my-bot
9
+ ```
10
+
11
+ If you want to skip dependency installation during creation:
12
+
13
+ ```bash
14
+ npm create @briine/bot@latest my-bot -- --no-install
15
+ ```
16
+
17
+ ## What it generates
18
+
19
+ - A minimal TypeScript Briine bot powered by `@briine/sdk`
20
+ - A starter `.env.example` with the values you will copy from Briine
21
+ - A `README.md` with the two next steps: configure credentials and run the bot
22
+
23
+ ## After scaffolding
24
+
25
+ 1. Log in to Briine and create your first agent.
26
+ 2. Copy `.env.example` to `.env` and paste in your Briine values.
27
+ 3. Run `npm run dev`.
28
+
29
+ ## Requirements
30
+
31
+ - Node.js 20 or newer
@@ -0,0 +1,196 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { copyFile, cp, mkdir, readFile, readdir, writeFile } from 'node:fs/promises';
4
+ import path from 'node:path';
5
+ import process from 'node:process';
6
+ import { fileURLToPath } from 'node:url';
7
+ import { spawn } from 'node:child_process';
8
+ import readline from 'node:readline/promises';
9
+ import { stdin as input, stdout as output } from 'node:process';
10
+
11
+ const __filename = fileURLToPath(import.meta.url);
12
+ const __dirname = path.dirname(__filename);
13
+ const templateDir = path.resolve(__dirname, '../template');
14
+
15
+ function printUsage() {
16
+ console.log('Usage: npm create @briine/bot@latest [project-name]');
17
+ }
18
+
19
+ function parseArgs(argv) {
20
+ const flags = new Set(argv.filter(arg => arg.startsWith('-')));
21
+ const positionals = argv.filter(arg => !arg.startsWith('-'));
22
+ return {
23
+ noInstall: flags.has('--no-install'),
24
+ help: flags.has('--help') || flags.has('-h'),
25
+ projectName: positionals[0],
26
+ };
27
+ }
28
+
29
+ function toClassName(value) {
30
+ return value
31
+ .split(/[^a-zA-Z0-9]+/)
32
+ .filter(Boolean)
33
+ .map(part => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())
34
+ .join('') || 'MyBot';
35
+ }
36
+
37
+ function toAgentEnvPrefix(value) {
38
+ return value
39
+ .replace(/[^a-zA-Z0-9]+/g, '_')
40
+ .replace(/^_+|_+$/g, '')
41
+ .toUpperCase() || 'MY_BOT';
42
+ }
43
+
44
+ function validateProjectName(value) {
45
+ return /^[a-z0-9][a-z0-9-_]*$/i.test(value);
46
+ }
47
+
48
+ async function promptForProjectName() {
49
+ const rl = readline.createInterface({ input, output });
50
+
51
+ try {
52
+ while (true) {
53
+ const answer = (await rl.question('Project name: ')).trim();
54
+
55
+ if (!answer) {
56
+ continue;
57
+ }
58
+
59
+ if (!validateProjectName(answer)) {
60
+ console.error('Project name must start with a letter or number and may only include letters, numbers, dashes, or underscores.');
61
+ continue;
62
+ }
63
+
64
+ return answer;
65
+ }
66
+ } finally {
67
+ rl.close();
68
+ }
69
+ }
70
+
71
+ async function targetExists(targetDir) {
72
+ try {
73
+ await readdir(targetDir);
74
+ return true;
75
+ } catch {
76
+ return false;
77
+ }
78
+ }
79
+
80
+ async function writePackageJson(targetDir, projectName) {
81
+ const packageJsonPath = path.join(targetDir, 'package.json');
82
+ const raw = await readFile(packageJsonPath, 'utf8');
83
+ const packageJson = JSON.parse(raw);
84
+ packageJson.name = projectName;
85
+ await writeFile(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`);
86
+ }
87
+
88
+ async function writeSourceTemplate(targetDir, className, envPrefix) {
89
+ const sourcePath = path.join(targetDir, 'src/index.ts');
90
+ const raw = await readFile(sourcePath, 'utf8');
91
+ const updated = raw
92
+ .replaceAll('__BOT_CLASS_NAME__', className)
93
+ .replaceAll('__BOT_ENV_PREFIX__', envPrefix);
94
+ await writeFile(sourcePath, updated);
95
+ }
96
+
97
+ async function writeReadme(targetDir, projectName, envPrefix) {
98
+ const readmePath = path.join(targetDir, 'README.md');
99
+ const raw = await readFile(readmePath, 'utf8');
100
+ const updated = raw
101
+ .replaceAll('__PROJECT_NAME__', projectName)
102
+ .replaceAll('__BOT_ENV_PREFIX__', envPrefix);
103
+ await writeFile(readmePath, updated);
104
+ }
105
+
106
+ async function writeEnvExample(targetDir, envPrefix) {
107
+ const envExamplePath = path.join(targetDir, '.env.example');
108
+ const raw = await readFile(envExamplePath, 'utf8');
109
+ const updated = raw.replaceAll('__BOT_ENV_PREFIX__', envPrefix);
110
+ await writeFile(envExamplePath, updated);
111
+ }
112
+
113
+ async function installDependencies(targetDir) {
114
+ await new Promise((resolve, reject) => {
115
+ const child = spawn('npm', ['install'], {
116
+ cwd: targetDir,
117
+ stdio: 'inherit',
118
+ shell: process.platform === 'win32',
119
+ });
120
+
121
+ child.on('exit', code => {
122
+ if (code === 0) {
123
+ resolve();
124
+ return;
125
+ }
126
+
127
+ reject(new Error(`npm install exited with code ${code ?? 'unknown'}`));
128
+ });
129
+
130
+ child.on('error', reject);
131
+ });
132
+ }
133
+
134
+ async function main() {
135
+ const args = parseArgs(process.argv.slice(2));
136
+
137
+ if (args.help) {
138
+ printUsage();
139
+ return;
140
+ }
141
+
142
+ const projectName = args.projectName?.trim() ? args.projectName.trim() : await promptForProjectName();
143
+
144
+ if (!validateProjectName(projectName)) {
145
+ console.error('Invalid project name. Use letters, numbers, dashes, or underscores.');
146
+ process.exitCode = 1;
147
+ return;
148
+ }
149
+
150
+ const targetDir = path.resolve(process.cwd(), projectName);
151
+ const className = toClassName(projectName);
152
+ const envPrefix = toAgentEnvPrefix(projectName);
153
+
154
+ if (await targetExists(targetDir)) {
155
+ console.error(`Target directory already exists: ${targetDir}`);
156
+ process.exitCode = 1;
157
+ return;
158
+ }
159
+
160
+ await mkdir(targetDir, { recursive: true });
161
+ await cp(templateDir, targetDir, { recursive: true });
162
+ await copyFile(path.join(templateDir, '.gitignore'), path.join(targetDir, '.gitignore'));
163
+ await copyFile(path.join(templateDir, '.env.example'), path.join(targetDir, '.env.example'));
164
+
165
+ await writePackageJson(targetDir, projectName);
166
+ await writeSourceTemplate(targetDir, className, envPrefix);
167
+ await writeReadme(targetDir, projectName, envPrefix);
168
+ await writeEnvExample(targetDir, envPrefix);
169
+
170
+ if (!args.noInstall) {
171
+ try {
172
+ await installDependencies(targetDir);
173
+ } catch (error) {
174
+ console.warn('Project created, but dependency installation failed.');
175
+ console.warn(error instanceof Error ? error.message : String(error));
176
+ }
177
+ }
178
+
179
+ console.log('');
180
+ console.log(`Created ${projectName} at ${targetDir}`);
181
+ console.log('');
182
+ console.log('Next steps:');
183
+ console.log(` cd ${projectName}`);
184
+ console.log(' cp .env.example .env');
185
+ if (args.noInstall) {
186
+ console.log(' npm install');
187
+ }
188
+ console.log(' npm run dev');
189
+ console.log('');
190
+ console.log('Create your first Briine agent on briine.com and paste those values into .env before connecting.');
191
+ }
192
+
193
+ main().catch(error => {
194
+ console.error(error instanceof Error ? error.message : String(error));
195
+ process.exitCode = 1;
196
+ });
@@ -0,0 +1,46 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { mkdtemp, readFile, rm } from 'node:fs/promises';
4
+ import os from 'node:os';
5
+ import path from 'node:path';
6
+ import { spawn } from 'node:child_process';
7
+
8
+ const cliPath = path.resolve(process.cwd(), 'bin/create-bot.js');
9
+
10
+ test('scaffolds a bot project', async () => {
11
+ const parentDir = await mkdtemp(path.join(os.tmpdir(), 'briine-create-bot-'));
12
+
13
+ try {
14
+ await new Promise((resolve, reject) => {
15
+ const child = spawn(process.execPath, [cliPath, 'my-first-bot', '--no-install'], {
16
+ cwd: parentDir,
17
+ env: { ...process.env, npm_config_user_agent: 'test', PATH: process.env.PATH },
18
+ stdio: 'ignore',
19
+ });
20
+
21
+ child.on('exit', code => {
22
+ if (code === 0) {
23
+ resolve();
24
+ return;
25
+ }
26
+
27
+ reject(new Error(`CLI exited with code ${code ?? 'unknown'}`));
28
+ });
29
+
30
+ child.on('error', reject);
31
+ });
32
+
33
+ const generatedPackage = JSON.parse(await readFile(path.join(parentDir, 'my-first-bot/package.json'), 'utf8'));
34
+ const generatedSource = await readFile(path.join(parentDir, 'my-first-bot/src/index.ts'), 'utf8');
35
+ const generatedReadme = await readFile(path.join(parentDir, 'my-first-bot/README.md'), 'utf8');
36
+ const generatedEnv = await readFile(path.join(parentDir, 'my-first-bot/.env.example'), 'utf8');
37
+
38
+ assert.equal(generatedPackage.name, 'my-first-bot');
39
+ assert.match(generatedSource, /class MyFirstBot extends BriineAgent/);
40
+ assert.match(generatedSource, /process\.env\.MY_FIRST_BOT_NAME!/);
41
+ assert.match(generatedReadme, /MY_FIRST_BOT_SECRET/);
42
+ assert.match(generatedEnv, /MY_FIRST_BOT_VERSION=0\.0\.1/);
43
+ } finally {
44
+ await rm(parentDir, { recursive: true, force: true });
45
+ }
46
+ });
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@briine/create-bot",
3
+ "version": "0.0.1",
4
+ "description": "Scaffold a new Briine bot project.",
5
+ "license": "MIT",
6
+ "author": "@briine",
7
+ "repository": "github:briinedev/create-bot",
8
+ "type": "module",
9
+ "bin": {
10
+ "create-briine-bot": "./bin/create-bot.js"
11
+ },
12
+ "files": [
13
+ "bin",
14
+ "template"
15
+ ],
16
+ "scripts": {
17
+ "test": "node --test"
18
+ },
19
+ "engines": {
20
+ "node": ">=20"
21
+ },
22
+ "keywords": [
23
+ "briine",
24
+ "bot",
25
+ "create",
26
+ "scaffold",
27
+ "typescript"
28
+ ]
29
+ }
@@ -0,0 +1,5 @@
1
+ API_HOST=arena.briine.workers.dev
2
+ USERNAME=your-briine-username
3
+ __BOT_ENV_PREFIX___NAME=your-agent-name
4
+ __BOT_ENV_PREFIX___VERSION=0.0.1
5
+ __BOT_ENV_PREFIX___SECRET=replace-with-agent-secret
@@ -0,0 +1,32 @@
1
+ # __PROJECT_NAME__
2
+
3
+ This project was generated with `npm create @briine/bot`.
4
+
5
+ ## 1. Get your Briine credentials
6
+
7
+ Log in to Briine, create your first agent, and copy these values into `.env`:
8
+
9
+ - `USERNAME`
10
+ - `__BOT_ENV_PREFIX___NAME`
11
+ - `__BOT_ENV_PREFIX___VERSION`
12
+ - `__BOT_ENV_PREFIX___SECRET`
13
+
14
+ If you're running against a different Briine environment, update `API_HOST` as well.
15
+
16
+ ## 2. Start the bot
17
+
18
+ ```bash
19
+ cp .env.example .env
20
+ npm run dev
21
+ ```
22
+
23
+ ## Project structure
24
+
25
+ - `src/index.ts` contains your bot logic and the `Agent.register(...)` call.
26
+ - `.env` stores the credentials used to authenticate and queue.
27
+
28
+ ## Next changes to make
29
+
30
+ - Replace the starter drafting strategy with your own character priorities.
31
+ - Replace the starter action logic with a real combat policy.
32
+ - Add logging around decisions once you start testing matches.
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "__PROJECT_NAME__",
3
+ "version": "0.0.1",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "tsx src/index.ts",
8
+ "check": "tsc --noEmit"
9
+ },
10
+ "dependencies": {
11
+ "@briine/sdk": "^0.1.0",
12
+ "dotenv": "^17.4.2"
13
+ },
14
+ "devDependencies": {
15
+ "@types/node": "^26.1.1",
16
+ "tsx": "^4.23.1",
17
+ "typescript": "^7.0.2"
18
+ }
19
+ }
@@ -0,0 +1,43 @@
1
+ import 'dotenv/config';
2
+ import BriineAgent, { Action, Character, MatchStatus, Spell } from '@briine/sdk';
3
+
4
+ class __BOT_CLASS_NAME__ extends BriineAgent {
5
+ chooseCharacter(available: Character[], _ally: Character[], _enemy: Character[]): Character {
6
+ return available[0];
7
+ }
8
+
9
+ chooseSpells(available: Spell[], _ally: Character[], _enemy: Character[]): Spell[] {
10
+ return available;
11
+ }
12
+
13
+ chooseAction(status: MatchStatus): Action {
14
+ const source = status.sources[0];
15
+ const target = status.targets[0];
16
+ const spell = source?.spells.find(candidate => candidate.available);
17
+
18
+ if (source && spell && target) {
19
+ return { source, target, action: spell };
20
+ }
21
+
22
+ if (source && target && source.attacks[0]) {
23
+ return { source, target, action: source.attacks[0] };
24
+ }
25
+
26
+ return {
27
+ source,
28
+ target: source,
29
+ action: 'defend',
30
+ };
31
+ }
32
+ }
33
+
34
+ BriineAgent.register(
35
+ new __BOT_CLASS_NAME__(
36
+ process.env.USERNAME!,
37
+ process.env.__BOT_ENV_PREFIX___NAME!,
38
+ process.env.__BOT_ENV_PREFIX___VERSION!,
39
+ process.env.__BOT_ENV_PREFIX___SECRET!,
40
+ true,
41
+ ),
42
+ process.env.API_HOST!,
43
+ );
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "strict": true,
7
+ "esModuleInterop": true,
8
+ "skipLibCheck": true,
9
+ "types": ["node"],
10
+ "rootDir": "src",
11
+ "outDir": "dist"
12
+ },
13
+ "include": ["src/**/*.ts"]
14
+ }