create-brutal-ai 0.0.0-stage → 0.2.0
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 +6 -2
- package/index.js +141 -0
- package/package.json +20 -4
package/README.md
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
-
#
|
|
1
|
+
# create-brutal-ai
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Deploy your own [Brutal](https://github.com/cldixon/brutal) to your Cloudflare account:
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
bun create brutal-ai my-brutal
|
|
7
|
+
```
|
package/index.js
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
// @bun
|
|
3
|
+
|
|
4
|
+
// packages/create-brutal-ai/src/index.ts
|
|
5
|
+
import { existsSync, mkdirSync, readdirSync, writeFileSync } from "fs";
|
|
6
|
+
import { dirname, join, resolve } from "path";
|
|
7
|
+
var {$ } = globalThis.Bun;
|
|
8
|
+
var args = process.argv.slice(2);
|
|
9
|
+
var flag = (name) => {
|
|
10
|
+
const i = args.indexOf(name);
|
|
11
|
+
return i === -1 ? undefined : args.splice(i, 2)[1];
|
|
12
|
+
};
|
|
13
|
+
var from = flag("--from");
|
|
14
|
+
var noDeploy = args.includes("--no-deploy");
|
|
15
|
+
var folder = args.find((a) => !a.startsWith("--")) ?? "my-brutal";
|
|
16
|
+
var dir = resolve(folder);
|
|
17
|
+
process.env.CF_SEND_TELEMETRY ??= "false";
|
|
18
|
+
if (existsSync(dir) && readdirSync(dir).length > 0) {
|
|
19
|
+
console.error(`${folder} already exists and isn't empty.`);
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
console.log(`
|
|
23
|
+
Creating a Brutal instance in ${folder}
|
|
24
|
+
`);
|
|
25
|
+
mkdirSync(dir, { recursive: true });
|
|
26
|
+
var files = {
|
|
27
|
+
"package.json": `${JSON.stringify({
|
|
28
|
+
name: folder.split("/").pop(),
|
|
29
|
+
private: true,
|
|
30
|
+
type: "module",
|
|
31
|
+
scripts: {
|
|
32
|
+
setup: "brutal-server setup",
|
|
33
|
+
deploy: "brutal-server deploy",
|
|
34
|
+
dev: "brutal-server dev",
|
|
35
|
+
secrets: "brutal-server secrets",
|
|
36
|
+
access: "brutal-server access"
|
|
37
|
+
},
|
|
38
|
+
dependencies: {
|
|
39
|
+
"@brutal-ai/server": from ? resolveSpec(from) : `^${"0.2.0"}`,
|
|
40
|
+
cf: "1.0.0-beta.28",
|
|
41
|
+
wrangler: "^4.149.0"
|
|
42
|
+
}
|
|
43
|
+
}, null, 2)}
|
|
44
|
+
`,
|
|
45
|
+
"cloudflare.config.ts": `import { defineBrutal } from "@brutal-ai/server/config";
|
|
46
|
+
|
|
47
|
+
// Your settings are in brutal.config.json (\`bun run setup\` writes it).
|
|
48
|
+
export default defineBrutal({ dir: import.meta.dirname });
|
|
49
|
+
`,
|
|
50
|
+
".github/workflows/deploy.yml": `name: Deploy
|
|
51
|
+
|
|
52
|
+
# Deploys this Brutal instance on every push to main, using the repo secret
|
|
53
|
+
# CLOUDFLARE_API_TOKEN. Without the secret, it skips with a notice.
|
|
54
|
+
on:
|
|
55
|
+
push:
|
|
56
|
+
branches: [main]
|
|
57
|
+
workflow_dispatch:
|
|
58
|
+
|
|
59
|
+
concurrency:
|
|
60
|
+
group: deploy
|
|
61
|
+
cancel-in-progress: false
|
|
62
|
+
|
|
63
|
+
jobs:
|
|
64
|
+
deploy:
|
|
65
|
+
runs-on: ubuntu-latest
|
|
66
|
+
env:
|
|
67
|
+
CLOUDFLARE_API_TOKEN: \${{ secrets.CLOUDFLARE_API_TOKEN }}
|
|
68
|
+
CF_SEND_TELEMETRY: "false"
|
|
69
|
+
steps:
|
|
70
|
+
- name: No token, no deploy
|
|
71
|
+
if: env.CLOUDFLARE_API_TOKEN == ''
|
|
72
|
+
run: echo "::notice::Not deploying. Add the CLOUDFLARE_API_TOKEN repo secret to deploy on push."
|
|
73
|
+
- uses: actions/checkout@v5
|
|
74
|
+
if: env.CLOUDFLARE_API_TOKEN != ''
|
|
75
|
+
- uses: oven-sh/setup-bun@v2
|
|
76
|
+
if: env.CLOUDFLARE_API_TOKEN != ''
|
|
77
|
+
- run: bun install --frozen-lockfile
|
|
78
|
+
if: env.CLOUDFLARE_API_TOKEN != ''
|
|
79
|
+
- run: bun run deploy
|
|
80
|
+
if: env.CLOUDFLARE_API_TOKEN != ''
|
|
81
|
+
`,
|
|
82
|
+
".gitignore": `node_modules/
|
|
83
|
+
.cloudflare/
|
|
84
|
+
.wrangler/
|
|
85
|
+
`,
|
|
86
|
+
"README.md": `# My Brutal
|
|
87
|
+
|
|
88
|
+
A [Brutal](https://github.com/cldixon/brutal) instance: feedback from your agents, on your own
|
|
89
|
+
Cloudflare account. Its settings are in \`brutal.config.json\`; the code comes from
|
|
90
|
+
[\`@brutal-ai/server\`](https://www.npmjs.com/package/@brutal-ai/server).
|
|
91
|
+
|
|
92
|
+
| Command | What it does |
|
|
93
|
+
| --- | --- |
|
|
94
|
+
| \`bun run setup\` | Change the settings (re-run any time) |
|
|
95
|
+
| \`bun run deploy\` | Deploy. The first deploy prints the setup code: open \`/setup\` and enter it for your keys |
|
|
96
|
+
| \`bun run dev\` | Run it locally on http://localhost:8787 |
|
|
97
|
+
| \`bun run access\` | Sign in to \`/dashboard\` with Cloudflare Access (a code mailed to you) instead of the read key; needs a custom domain. Then \`bun run deploy\` |
|
|
98
|
+
|
|
99
|
+
**Update Brutal:** \`bun update @brutal-ai/server && bun run deploy\`. Migrations apply themselves.
|
|
100
|
+
|
|
101
|
+
**Deploy on push (optional):** \`.github/workflows/deploy.yml\` deploys every push to \`main\` once
|
|
102
|
+
this repo has the secret \`CLOUDFLARE_API_TOKEN\` (a Cloudflare API token from the "Edit Cloudflare
|
|
103
|
+
Workers" template, plus D1 Edit). Until then it skips, and \`bun run deploy\` from your machine is
|
|
104
|
+
all you need.
|
|
105
|
+
`
|
|
106
|
+
};
|
|
107
|
+
for (const [name, content] of Object.entries(files)) {
|
|
108
|
+
mkdirSync(dirname(join(dir, name)), { recursive: true });
|
|
109
|
+
writeFileSync(join(dir, name), content);
|
|
110
|
+
}
|
|
111
|
+
console.log("\u2713 Wrote the instance folder");
|
|
112
|
+
await $`bun install`.cwd(dir).quiet();
|
|
113
|
+
console.log("\u2713 Installed @brutal-ai/server");
|
|
114
|
+
if ((await $`git --version`.nothrow().quiet()).exitCode === 0) {
|
|
115
|
+
await $`git init -q`.cwd(dir).nothrow();
|
|
116
|
+
}
|
|
117
|
+
var whoami = await $`bunx cf auth whoami`.cwd(dir).nothrow().quiet();
|
|
118
|
+
var signedIn = false;
|
|
119
|
+
try {
|
|
120
|
+
signedIn = whoami.exitCode === 0 && JSON.parse(whoami.stdout.toString()).authenticated;
|
|
121
|
+
} catch {}
|
|
122
|
+
if (!signedIn) {
|
|
123
|
+
console.log(`
|
|
124
|
+
Sign in to Cloudflare (a browser window opens):`);
|
|
125
|
+
await interactive(["bunx", "cf", "auth", "login"]);
|
|
126
|
+
}
|
|
127
|
+
await interactive(["bunx", "brutal-server", "setup"]);
|
|
128
|
+
var answer = noDeploy ? null : prompt("Deploy it now? [Y/n]");
|
|
129
|
+
if (answer !== null && answer.trim().toLowerCase() !== "n") {
|
|
130
|
+
await interactive(["bunx", "brutal-server", "deploy"]);
|
|
131
|
+
}
|
|
132
|
+
console.log(`Done. Your instance lives in ${folder}: commit it, and deploy with \`bun run deploy\`.
|
|
133
|
+
`);
|
|
134
|
+
async function interactive(cmd) {
|
|
135
|
+
const code = await Bun.spawn(cmd, { cwd: dir, stdio: ["inherit", "inherit", "inherit"] }).exited;
|
|
136
|
+
if (code !== 0)
|
|
137
|
+
process.exit(code);
|
|
138
|
+
}
|
|
139
|
+
function resolveSpec(spec) {
|
|
140
|
+
return /^(\.|\/).*\.tgz$/.test(spec) ? resolve(spec) : spec;
|
|
141
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
{
|
|
2
|
+
"version": "0.2.0",
|
|
3
|
+
"license": "MIT",
|
|
2
4
|
"name": "create-brutal-ai",
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
5
|
+
"description": "Create your own Brutal instance on your Cloudflare account",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/cldixon/brutal.git",
|
|
10
|
+
"directory": "packages/create-brutal-ai"
|
|
11
|
+
},
|
|
12
|
+
"bin": {
|
|
13
|
+
"create-brutal-ai": "index.js"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"index.js",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"engines": {
|
|
20
|
+
"bun": ">=1.2.0"
|
|
21
|
+
}
|
|
22
|
+
}
|