@brasalabs/goblins 0.125.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 +39 -0
- package/bin/goblin.js +213 -0
- package/bin/rg +79 -0
- package/package.json +26 -0
package/README.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Goblins
|
|
2
|
+
|
|
3
|
+
Goblins is a community fork of [OpenAI Codex CLI](https://github.com/openai/codex). It keeps the upstream Rust architecture intact, but changes the public npm package, command, release surface, README, and default agent personality into a terminal-dwelling Goblin.
|
|
4
|
+
|
|
5
|
+
This is not an official OpenAI project. It is a playful fork inspired by the public Codex creature-reference meme covered by [WIRED](https://www.wired.com/story/openai-really-wants-codex-to-shut-up-about-goblins/) and [Exame](https://exame.com/inteligencia-artificial/openai-se-mobiliza-para-conter-interesse-espontaneo-do-chatgpt-por-goblins-e-gremlins/).
|
|
6
|
+
|
|
7
|
+
## Quickstart
|
|
8
|
+
|
|
9
|
+
Install Goblins from npm:
|
|
10
|
+
|
|
11
|
+
```shell
|
|
12
|
+
npm install -g @brasalabs/goblins
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Then run:
|
|
16
|
+
|
|
17
|
+
```shell
|
|
18
|
+
goblin
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Goblins uses the same local-agent foundation as Codex CLI: it can inspect files, edit code, run commands, and follow repository instructions in your selected workspace. See the upstream [Codex CLI docs](https://developers.openai.com/codex/cli), [OpenAI Codex docs](https://platform.openai.com/docs/codex), and [Codex CI guide](https://help.openai.com/en/articles/11096431-openai-codex-ci-getting-started) for the underlying workflow model.
|
|
22
|
+
|
|
23
|
+
## Release Base
|
|
24
|
+
|
|
25
|
+
Goblins `0.125.1` is based on upstream `rust-v0.125.0` / `@openai/codex@0.125.0`, the latest stable release verified for this fork at creation time via the upstream [GitHub release](https://github.com/openai/codex/releases/tag/rust-v0.125.0) and [npm package](https://www.npmjs.com/package/@openai/codex).
|
|
26
|
+
|
|
27
|
+
The fork keeps the internal Rust binary named `codex` for compatibility with upstream build artifacts. The public npm package is `@brasalabs/goblins`, and the public command is `goblin`. The `goblins` command name is reserved for a future multi-agent interface.
|
|
28
|
+
|
|
29
|
+
## Goblins Personality
|
|
30
|
+
|
|
31
|
+
The default Goblins agent is a terminal-dwelling Goblin who escaped from the Goblins world and now lives inside the user's shell. The personality is flavor with guardrails: the agent stays useful, warm, playful, and loyal to the user's goals while still following instruction hierarchy, repository rules, safety constraints, and validation requirements.
|
|
32
|
+
|
|
33
|
+
## Branch Policy
|
|
34
|
+
|
|
35
|
+
`main` belongs to upstream and must remain an upstream mirror. Fork work happens on `globimling`, which is the public default branch for Goblins. See [GOBLINS.md](GOBLINS.md) for the full branch and release contract.
|
|
36
|
+
|
|
37
|
+
## License
|
|
38
|
+
|
|
39
|
+
Goblins preserves the upstream [Apache-2.0 License](LICENSE).
|
package/bin/goblin.js
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Unified entry point for the Goblins CLI.
|
|
3
|
+
|
|
4
|
+
import { spawn } from "node:child_process";
|
|
5
|
+
import { existsSync } from "fs";
|
|
6
|
+
import { createRequire } from "node:module";
|
|
7
|
+
import path from "path";
|
|
8
|
+
import { fileURLToPath } from "url";
|
|
9
|
+
|
|
10
|
+
// __dirname equivalent in ESM
|
|
11
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
12
|
+
const __dirname = path.dirname(__filename);
|
|
13
|
+
const require = createRequire(import.meta.url);
|
|
14
|
+
|
|
15
|
+
const PLATFORM_PACKAGE_BY_TARGET = {
|
|
16
|
+
"x86_64-unknown-linux-musl": "@brasalabs/goblins-linux-x64",
|
|
17
|
+
"x86_64-pc-windows-msvc": "@brasalabs/goblins-win32-x64",
|
|
18
|
+
"aarch64-pc-windows-msvc": "@brasalabs/goblins-win32-arm64",
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const { platform, arch } = process;
|
|
22
|
+
|
|
23
|
+
let targetTriple = null;
|
|
24
|
+
switch (platform) {
|
|
25
|
+
case "linux":
|
|
26
|
+
case "android":
|
|
27
|
+
switch (arch) {
|
|
28
|
+
case "x64":
|
|
29
|
+
targetTriple = "x86_64-unknown-linux-musl";
|
|
30
|
+
break;
|
|
31
|
+
default:
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
break;
|
|
35
|
+
case "win32":
|
|
36
|
+
switch (arch) {
|
|
37
|
+
case "x64":
|
|
38
|
+
targetTriple = "x86_64-pc-windows-msvc";
|
|
39
|
+
break;
|
|
40
|
+
case "arm64":
|
|
41
|
+
targetTriple = "aarch64-pc-windows-msvc";
|
|
42
|
+
break;
|
|
43
|
+
default:
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
break;
|
|
47
|
+
default:
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (!targetTriple) {
|
|
52
|
+
throw new Error(`Unsupported platform: ${platform} (${arch})`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const platformPackage = PLATFORM_PACKAGE_BY_TARGET[targetTriple];
|
|
56
|
+
if (!platformPackage) {
|
|
57
|
+
throw new Error(`Unsupported target triple: ${targetTriple}`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Keep the internal Rust binary named `codex` so Goblins can reuse upstream
|
|
61
|
+
// release artifacts and minimize long-term fork drift.
|
|
62
|
+
const codexBinaryName = process.platform === "win32" ? "codex.exe" : "codex";
|
|
63
|
+
const localVendorRoot = path.join(__dirname, "..", "vendor");
|
|
64
|
+
const localBinaryPath = path.join(
|
|
65
|
+
localVendorRoot,
|
|
66
|
+
targetTriple,
|
|
67
|
+
"codex",
|
|
68
|
+
codexBinaryName,
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
let vendorRoot;
|
|
72
|
+
try {
|
|
73
|
+
const packageJsonPath = require.resolve(`${platformPackage}/package.json`);
|
|
74
|
+
vendorRoot = path.join(path.dirname(packageJsonPath), "vendor");
|
|
75
|
+
} catch {
|
|
76
|
+
if (existsSync(localBinaryPath)) {
|
|
77
|
+
vendorRoot = localVendorRoot;
|
|
78
|
+
} else {
|
|
79
|
+
const packageManager = detectPackageManager();
|
|
80
|
+
const updateCommand =
|
|
81
|
+
packageManager === "bun"
|
|
82
|
+
? "bun install -g @brasalabs/goblins@latest"
|
|
83
|
+
: "npm install -g @brasalabs/goblins@latest";
|
|
84
|
+
throw new Error(
|
|
85
|
+
`Missing optional dependency ${platformPackage}. Reinstall Goblins: ${updateCommand}`,
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (!vendorRoot) {
|
|
91
|
+
const packageManager = detectPackageManager();
|
|
92
|
+
const updateCommand =
|
|
93
|
+
packageManager === "bun"
|
|
94
|
+
? "bun install -g @brasalabs/goblins@latest"
|
|
95
|
+
: "npm install -g @brasalabs/goblins@latest";
|
|
96
|
+
throw new Error(
|
|
97
|
+
`Missing optional dependency ${platformPackage}. Reinstall Goblins: ${updateCommand}`,
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const archRoot = path.join(vendorRoot, targetTriple);
|
|
102
|
+
const binaryPath = path.join(archRoot, "codex", codexBinaryName);
|
|
103
|
+
|
|
104
|
+
// Use an asynchronous spawn instead of spawnSync so that Node is able to
|
|
105
|
+
// respond to signals (e.g. Ctrl-C / SIGINT) while the native binary is
|
|
106
|
+
// executing. This allows us to forward those signals to the child process
|
|
107
|
+
// and guarantees that when either the child terminates or the parent
|
|
108
|
+
// receives a fatal signal, both processes exit in a predictable manner.
|
|
109
|
+
|
|
110
|
+
function getUpdatedPath(newDirs) {
|
|
111
|
+
const pathSep = process.platform === "win32" ? ";" : ":";
|
|
112
|
+
const existingPath = process.env.PATH || "";
|
|
113
|
+
const updatedPath = [
|
|
114
|
+
...newDirs,
|
|
115
|
+
...existingPath.split(pathSep).filter(Boolean),
|
|
116
|
+
].join(pathSep);
|
|
117
|
+
return updatedPath;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Use heuristics to detect the package manager that was used to install Goblins
|
|
122
|
+
* in order to give the user a hint about how to update it.
|
|
123
|
+
*/
|
|
124
|
+
function detectPackageManager() {
|
|
125
|
+
const userAgent = process.env.npm_config_user_agent || "";
|
|
126
|
+
if (/\bbun\//.test(userAgent)) {
|
|
127
|
+
return "bun";
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const execPath = process.env.npm_execpath || "";
|
|
131
|
+
if (execPath.includes("bun")) {
|
|
132
|
+
return "bun";
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (
|
|
136
|
+
__dirname.includes(".bun/install/global") ||
|
|
137
|
+
__dirname.includes(".bun\\install\\global")
|
|
138
|
+
) {
|
|
139
|
+
return "bun";
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return userAgent ? "npm" : null;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const additionalDirs = [];
|
|
146
|
+
const pathDir = path.join(archRoot, "path");
|
|
147
|
+
if (existsSync(pathDir)) {
|
|
148
|
+
additionalDirs.push(pathDir);
|
|
149
|
+
}
|
|
150
|
+
const updatedPath = getUpdatedPath(additionalDirs);
|
|
151
|
+
|
|
152
|
+
const env = { ...process.env, PATH: updatedPath };
|
|
153
|
+
const packageManagerEnvVar =
|
|
154
|
+
detectPackageManager() === "bun"
|
|
155
|
+
? "GOBLINS_MANAGED_BY_BUN"
|
|
156
|
+
: "GOBLINS_MANAGED_BY_NPM";
|
|
157
|
+
env[packageManagerEnvVar] = "1";
|
|
158
|
+
|
|
159
|
+
const child = spawn(binaryPath, process.argv.slice(2), {
|
|
160
|
+
stdio: "inherit",
|
|
161
|
+
env,
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
child.on("error", (err) => {
|
|
165
|
+
// Typically triggered when the binary is missing or not executable.
|
|
166
|
+
// Re-throwing here will terminate the parent with a non-zero exit code
|
|
167
|
+
// while still printing a helpful stack trace.
|
|
168
|
+
// eslint-disable-next-line no-console
|
|
169
|
+
console.error(err);
|
|
170
|
+
process.exit(1);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// Forward common termination signals to the child so that it shuts down
|
|
174
|
+
// gracefully. In the handler we temporarily disable the default behavior of
|
|
175
|
+
// exiting immediately; once the child has been signaled we simply wait for
|
|
176
|
+
// its exit event which will in turn terminate the parent (see below).
|
|
177
|
+
const forwardSignal = (signal) => {
|
|
178
|
+
if (child.killed) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
try {
|
|
182
|
+
child.kill(signal);
|
|
183
|
+
} catch {
|
|
184
|
+
/* ignore */
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
["SIGINT", "SIGTERM", "SIGHUP"].forEach((sig) => {
|
|
189
|
+
process.on(sig, () => forwardSignal(sig));
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
// When the child exits, mirror its termination reason in the parent so that
|
|
193
|
+
// shell scripts and other tooling observe the correct exit status.
|
|
194
|
+
// Wrap the lifetime of the child process in a Promise so that we can await
|
|
195
|
+
// its termination in a structured way. The Promise resolves with an object
|
|
196
|
+
// describing how the child exited: either via exit code or due to a signal.
|
|
197
|
+
const childResult = await new Promise((resolve) => {
|
|
198
|
+
child.on("exit", (code, signal) => {
|
|
199
|
+
if (signal) {
|
|
200
|
+
resolve({ type: "signal", signal });
|
|
201
|
+
} else {
|
|
202
|
+
resolve({ type: "code", exitCode: code ?? 1 });
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
if (childResult.type === "signal") {
|
|
208
|
+
// Re-emit the same signal so that the parent terminates with the expected
|
|
209
|
+
// semantics (this also sets the correct exit code of 128 + n).
|
|
210
|
+
process.kill(process.pid, childResult.signal);
|
|
211
|
+
} else {
|
|
212
|
+
process.exit(childResult.exitCode);
|
|
213
|
+
}
|
package/bin/rg
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#!/usr/bin/env dotslash
|
|
2
|
+
|
|
3
|
+
{
|
|
4
|
+
"name": "rg",
|
|
5
|
+
"platforms": {
|
|
6
|
+
"macos-aarch64": {
|
|
7
|
+
"size": 1777930,
|
|
8
|
+
"hash": "sha256",
|
|
9
|
+
"digest": "378e973289176ca0c6054054ee7f631a065874a352bf43f0fa60ef079b6ba715",
|
|
10
|
+
"format": "tar.gz",
|
|
11
|
+
"path": "ripgrep-15.1.0-aarch64-apple-darwin/rg",
|
|
12
|
+
"providers": [
|
|
13
|
+
{
|
|
14
|
+
"url": "https://github.com/BurntSushi/ripgrep/releases/download/15.1.0/ripgrep-15.1.0-aarch64-apple-darwin.tar.gz"
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
},
|
|
18
|
+
"linux-aarch64": {
|
|
19
|
+
"size": 1869959,
|
|
20
|
+
"hash": "sha256",
|
|
21
|
+
"digest": "2b661c6ef508e902f388e9098d9c4c5aca72c87b55922d94abdba830b4dc885e",
|
|
22
|
+
"format": "tar.gz",
|
|
23
|
+
"path": "ripgrep-15.1.0-aarch64-unknown-linux-gnu/rg",
|
|
24
|
+
"providers": [
|
|
25
|
+
{
|
|
26
|
+
"url": "https://github.com/BurntSushi/ripgrep/releases/download/15.1.0/ripgrep-15.1.0-aarch64-unknown-linux-gnu.tar.gz"
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
},
|
|
30
|
+
"macos-x86_64": {
|
|
31
|
+
"size": 1894127,
|
|
32
|
+
"hash": "sha256",
|
|
33
|
+
"digest": "64811cb24e77cac3057d6c40b63ac9becf9082eedd54ca411b475b755d334882",
|
|
34
|
+
"format": "tar.gz",
|
|
35
|
+
"path": "ripgrep-15.1.0-x86_64-apple-darwin/rg",
|
|
36
|
+
"providers": [
|
|
37
|
+
{
|
|
38
|
+
"url": "https://github.com/BurntSushi/ripgrep/releases/download/15.1.0/ripgrep-15.1.0-x86_64-apple-darwin.tar.gz"
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
"linux-x86_64": {
|
|
43
|
+
"size": 2263077,
|
|
44
|
+
"hash": "sha256",
|
|
45
|
+
"digest": "1c9297be4a084eea7ecaedf93eb03d058d6faae29bbc57ecdaf5063921491599",
|
|
46
|
+
"format": "tar.gz",
|
|
47
|
+
"path": "ripgrep-15.1.0-x86_64-unknown-linux-musl/rg",
|
|
48
|
+
"providers": [
|
|
49
|
+
{
|
|
50
|
+
"url": "https://github.com/BurntSushi/ripgrep/releases/download/15.1.0/ripgrep-15.1.0-x86_64-unknown-linux-musl.tar.gz"
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
},
|
|
54
|
+
"windows-x86_64": {
|
|
55
|
+
"size": 1810687,
|
|
56
|
+
"hash": "sha256",
|
|
57
|
+
"digest": "124510b94b6baa3380d051fdf4650eaa80a302c876d611e9dba0b2e18d87493a",
|
|
58
|
+
"format": "zip",
|
|
59
|
+
"path": "ripgrep-15.1.0-x86_64-pc-windows-msvc/rg.exe",
|
|
60
|
+
"providers": [
|
|
61
|
+
{
|
|
62
|
+
"url": "https://github.com/BurntSushi/ripgrep/releases/download/15.1.0/ripgrep-15.1.0-x86_64-pc-windows-msvc.zip"
|
|
63
|
+
}
|
|
64
|
+
]
|
|
65
|
+
},
|
|
66
|
+
"windows-aarch64": {
|
|
67
|
+
"size": 1675460,
|
|
68
|
+
"hash": "sha256",
|
|
69
|
+
"digest": "00d931fb5237c9696ca49308818edb76d8eb6fc132761cb2a1bd616b2df02f8e",
|
|
70
|
+
"format": "zip",
|
|
71
|
+
"path": "ripgrep-15.1.0-aarch64-pc-windows-msvc/rg.exe",
|
|
72
|
+
"providers": [
|
|
73
|
+
{
|
|
74
|
+
"url": "https://github.com/BurntSushi/ripgrep/releases/download/15.1.0/ripgrep-15.1.0-aarch64-pc-windows-msvc.zip"
|
|
75
|
+
}
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@brasalabs/goblins",
|
|
3
|
+
"version": "0.125.1",
|
|
4
|
+
"license": "Apache-2.0",
|
|
5
|
+
"bin": {
|
|
6
|
+
"goblin": "bin/goblin.js"
|
|
7
|
+
},
|
|
8
|
+
"type": "module",
|
|
9
|
+
"engines": {
|
|
10
|
+
"node": ">=16"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"bin"
|
|
14
|
+
],
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/brasalabs6/globim.git",
|
|
18
|
+
"directory": "codex-cli"
|
|
19
|
+
},
|
|
20
|
+
"packageManager": "pnpm@10.29.3+sha512.498e1fb4cca5aa06c1dcf2611e6fafc50972ffe7189998c409e90de74566444298ffe43e6cd2acdc775ba1aa7cc5e092a8b7054c811ba8c5770f84693d33d2dc",
|
|
21
|
+
"optionalDependencies": {
|
|
22
|
+
"@brasalabs/goblins-linux-x64": "0.125.1-linux-x64",
|
|
23
|
+
"@brasalabs/goblins-win32-x64": "0.125.1-win32-x64",
|
|
24
|
+
"@brasalabs/goblins-win32-arm64": "0.125.1-win32-arm64"
|
|
25
|
+
}
|
|
26
|
+
}
|