cybercode-cli 1.0.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/LICENSE ADDED
@@ -0,0 +1,53 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025-2026 webui_codex contributors
4
+
5
+ This project's agent core (agent_core.py) was developed with reference to the
6
+ GenericAgent project (https://github.com/lsdefine/GenericAgent), which is also
7
+ licensed under the MIT License:
8
+
9
+ MIT License
10
+
11
+ Copyright (c) 2025 lsdefine
12
+
13
+ Permission is hereby granted, free of charge, to any person obtaining a copy
14
+ of this software and associated documentation files (the "Software"), to deal
15
+ in the Software without restriction, including without limitation the rights
16
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
+ copies of the Software, and to permit persons to whom the Software is
18
+ furnished to do so, subject to the following conditions:
19
+
20
+ The above copyright notice and this permission notice shall be included in all
21
+ copies or substantial portions of the Software.
22
+
23
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
+ SOFTWARE.
30
+
31
+ The HyperFrames skills bundled in the skills/ directory are from the
32
+ HyperFrames project (https://github.com/heygen-com/hyperframes), also MIT
33
+ licensed. See individual skill files for their original copyright notices.
34
+
35
+ ---
36
+
37
+ Permission is hereby granted, free of charge, to any person obtaining a copy
38
+ of this software and associated documentation files (the "Software"), to deal
39
+ in the Software without restriction, including without limitation the rights
40
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
41
+ copies of the Software, and to permit persons to whom the Software is
42
+ furnished to do so, subject to the following conditions:
43
+
44
+ The above copyright notice and this permission notice shall be included in all
45
+ copies or substantial portions of the Software.
46
+
47
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
48
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
49
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
50
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
51
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
52
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
53
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,174 @@
1
+ # webui_codex — Codex-dark Web UI with a Built-in Self-Evolving Agent
2
+
3
+ A standalone, self-contained web UI + agent framework styled after the
4
+ Codex-dark interface. **No external agent dependency** — the entire agent
5
+ core (LLM client, agent loop, 9 atomic tools, layered memory) ships inside
6
+ `agent_core.py` and runs from a single directory.
7
+
8
+ ![cybercode](https://img.shields.io/badge/npm-cybercode-6b8cff) ![deps](https://img.shields.io/badge/dependencies-stdlib+requests-28c840) ![py](https://img.shields.io/badge/python-3.11%2B-blue) ![license](https://img.shields.io/badge/license-MIT-blue)
9
+
10
+ ## Quick Start
11
+
12
+ ```bash
13
+ npx cybercode
14
+ ```
15
+
16
+ That's it. On first run it:
17
+ 1. Finds Python 3.11+ on your system
18
+ 2. Installs `requests` if missing
19
+ 3. Copies the bundled agent + skills to `~/.cybercode/`
20
+ 4. Creates a `mykey.json` template (edit it with your API key)
21
+ 5. Starts the web UI and opens your browser
22
+
23
+ ## Attribution & License
24
+
25
+ The agent core architecture in `agent_core.py` was developed with reference to
26
+ [**GenericAgent**](https://github.com/lsdefine/GenericAgent) by lsdefine
27
+ (MIT License, Copyright © 2025 lsdefine). We gratefully acknowledge the
28
+ GenericAgent project — its ~100-line agent loop, 9-atomic-tool design, and
29
+ layered memory concept directly inspired this implementation.
30
+
31
+ The bundled HyperFrames skills (`skills/`) originate from
32
+ [**HyperFrames**](https://github.com/heygen-com/hyperframes) by HeyGen
33
+ (MIT License).
34
+
35
+ See `LICENSE` for the full MIT license text, including the GenericAgent
36
+ copyright notice as required by its license terms.
37
+
38
+ ## What it is
39
+
40
+ `webui_codex.py` boots a self-contained `Agent` (from `agent_core.py`), serves
41
+ `webui_codex.html` at `/`, and exposes a JSON + SSE API the page talks to.
42
+ The UI keeps the Codex-dark look — blue radial-gradient desktop, traffic-light
43
+ window chrome, dark sidebar with threads + skills, centered *Let's build*
44
+ empty state, pill LLM switcher, rounded composer with Local/Worktree/Cloud
45
+ modes — but every control is wired to the built-in agent.
46
+
47
+ **Standalone: no GenericAgent dependency.** The agent core (`agent_core.py`)
48
+ is a from-scratch reimplementation that ships in this repo. You do **not** need
49
+ GenericAgent installed. The only runtime dependency is `requests` (for the LLM
50
+ HTTP client); everything else uses the Python stdlib.
51
+
52
+ ## Files
53
+
54
+ ```
55
+ webui-codex/
56
+ ├── package.json # npm package config (bin, files, metadata)
57
+ ├── bin/
58
+ │ └── cli.mjs # Node.js launcher (finds Python, bootstraps, opens browser)
59
+ ├── python/
60
+ │ ├── agent_core.py # Self-contained agent core (LLM client + loop + 9 tools + memory)
61
+ │ ├── webui_codex.py # stdlib HTTP server + SSE + API
62
+ │ └── webui_codex.html # Codex-dark UI (single file, no build step)
63
+ ├── skills/ # HyperFrames video skills (12 files, from HeyGen)
64
+ ├── templates/
65
+ │ └── mykey_template.json # API key template (auto-copied on first run)
66
+ ├── LICENSE # MIT (includes GenericAgent + HyperFrames copyright notices)
67
+ └── README.md # This file
68
+ ```
69
+
70
+ ## Manual (without npx)
71
+
72
+ If you prefer to run the Python server directly:
73
+
74
+ ```bash
75
+ # 1. Configure your LLM (any OpenAI-compatible endpoint)
76
+ cat > mykey.json << 'EOF'
77
+ {
78
+ "llm1": {
79
+ "apikey": "sk-your-key-here",
80
+ "apibase": "https://api.openai.com",
81
+ "model": "gpt-4o",
82
+ "name": "GPT-4o"
83
+ }
84
+ }
85
+ EOF
86
+
87
+ # 2. Install requests (only dependency)
88
+ pip install requests
89
+
90
+ # 3. Run
91
+ python python/webui_codex.py # http://127.0.0.1:18600
92
+ ```
93
+
94
+ ## Supported LLM providers
95
+
96
+ Any OpenAI-compatible chat completions endpoint works:
97
+ - **OpenAI** (GPT-4o, o1, o3, etc.)
98
+ - **DeepSeek**
99
+ - **Kimi / Moonshot**
100
+ - **MiniMax**
101
+ - **Local models** via Ollama, LM Studio, vLLM, etc.
102
+ - **Anthropic Claude** via OpenAI-compatible proxies
103
+
104
+ ## What works
105
+
106
+ | UI element | Wired to |
107
+ | :--- | :--- |
108
+ | **Composer + send** | `agent.put_task()` → SSE stream of deltas + final `done` |
109
+ | **Stop button / red send toggle** | `agent.abort()` |
110
+ | **New thread** | `/api/new` → clears conversation history |
111
+ | **Thread list (sidebar)** | `/api/sessions` → scans `temp/model_responses/` logs |
112
+ | **Click a thread** | `/api/continue` → restores that session + replays bubbles |
113
+ | **LLM pill + dropdown** | `/api/llm` → `agent.next_llm(idx)` |
114
+ | **Skills panel (sidebar)** | `/api/skills` → lists `memory/` SOPs and skill files |
115
+ | **Suggest tasks** | prefills the autonomous-planning prompt |
116
+ | **Status dot** | `agent.is_running` polled every 4s |
117
+ | **Tool / file refs** | `🛠️ Tool: \`name\`` lines → tool chips; `[FILE:path]` → clickable file chips |
118
+ | **Turn folding** | `**LLM Running (Turn N) ...**` markers → collapsible turn sections |
119
+ | **Make video button** | Injects HyperFrames skill preamble + `/video` command |
120
+ | **Video gallery** | `/api/videos` → scans for rendered `.mp4` files |
121
+ | **Video playback** | `/api/video/<path>` → streams with Range support (seekable) |
122
+
123
+ ## The 9 Atomic Tools
124
+
125
+ The agent core exposes exactly 9 tools (same design philosophy as GenericAgent):
126
+
127
+ | Tool | Function |
128
+ | :--- | :--- |
129
+ | `code_run` | Execute Python or shell scripts |
130
+ | `file_read` | Read files with line numbers, keyword search |
131
+ | `file_write` | Create / overwrite / append files |
132
+ | `file_patch` | Patch a unique text block in a file |
133
+ | `web_scan` | Fetch and simplify web pages (urllib-based) |
134
+ | `web_execute_js` | Execute JS in a browser (requires TMWebDriver; degrades gracefully) |
135
+ | `ask_user` | Interrupt to ask the user a question |
136
+ | `update_working_checkpoint` | Short-term working memory notepad |
137
+ | `start_long_term_update` | Distill experience into long-term memory |
138
+
139
+ ## HyperFrames — Built-in Video Generation
140
+
141
+ The HyperFrames skill pack (12 files) is bundled in `skills/`. Click the
142
+ **Make video** button in the sidebar, or type `/video <description>`. The agent
143
+ reads the bundled skills and uses the `npx hyperframes` CLI to render HTML
144
+ compositions into `.mp4` files. Rendered videos appear in the sidebar gallery
145
+ and can be played inline.
146
+
147
+ ## API Reference
148
+
149
+ | Method | Path | Description |
150
+ | :--- | :--- | :--- |
151
+ | GET | `/` | The web UI |
152
+ | GET | `/api/status` | Agent status: running, LLM, history |
153
+ | GET | `/api/sessions` | Recoverable sessions (log files) |
154
+ | GET | `/api/skills` | Agent memory/SOP files |
155
+ | GET | `/api/messages?path=...` | Replay messages from a session log |
156
+ | GET | `/api/hyperframes` | List bundled HyperFrames skills |
157
+ | GET | `/api/hyperframes/<name>` | Raw skill markdown |
158
+ | GET | `/api/videos` | Rendered `.mp4` files |
159
+ | GET | `/api/video/<path>` | Stream a video (Range-supported) |
160
+ | POST | `/api/chat` | SSE stream: delta / done / error |
161
+ | POST | `/api/llm` | Switch LLM by index |
162
+ | POST | `/api/stop` | Abort current task |
163
+ | POST | `/api/new` | Start fresh conversation |
164
+ | POST | `/api/continue` | Restore session N |
165
+ | POST | `/api/btw` | Side question (while task runs) |
166
+
167
+ ## License
168
+
169
+ MIT License — see `LICENSE` for full text.
170
+
171
+ This project's agent core was developed with reference to
172
+ [GenericAgent](https://github.com/lsdefine/GenericAgent) (MIT, Copyright © 2025
173
+ lsdefine). The GenericAgent copyright notice is included in `LICENSE` as
174
+ required by its license terms. HyperFrames skills are MIT-licensed by HeyGen.
package/bin/cli.mjs ADDED
@@ -0,0 +1,314 @@
1
+ #!/usr/bin/env node
2
+ // cybercode CLI launcher — bootstraps a Python env and starts the web UI.
3
+ // Designed for `npx cybercode` one-click usage.
4
+
5
+ import { spawn, spawnSync, execSync } from "node:child_process";
6
+ import { existsSync, mkdirSync, copyFileSync, readdirSync, statSync, writeFileSync, readFileSync } from "node:fs";
7
+ import { join, dirname, resolve } from "node:path";
8
+ import { homedir, platform } from "node:os";
9
+ import { fileURLToPath } from "node:url";
10
+ import { createRequire } from "node:module";
11
+
12
+ const __dirname = dirname(fileURLToPath(import.meta.url));
13
+ const require = createRequire(import.meta.url);
14
+ const pkg = require("../package.json");
15
+
16
+ const COLORS = {
17
+ reset: "\x1b[0m", bold: "\x1b[1m", dim: "\x1b[2m",
18
+ green: "\x1b[32m", yellow: "\x1b[33m", blue: "\x1b[34m",
19
+ red: "\x1b[31m", cyan: "\x1b[36m",
20
+ };
21
+ const c = (color, text) => `${COLORS[color] || ""}${text}${COLORS.reset}`;
22
+
23
+ // ---- Parse CLI args ----
24
+ function parseArgs() {
25
+ const args = { port: null, host: "127.0.0.1", dir: null, noBrowser: false, llm: 0, help: false, version: false };
26
+ const argv = process.argv.slice(2);
27
+ for (let i = 0; i < argv.length; i++) {
28
+ const a = argv[i];
29
+ if (a === "-h" || a === "--help") args.help = true;
30
+ else if (a === "-V" || a === "--version") args.version = true;
31
+ else if (a === "-p" || a === "--port") args.port = parseInt(argv[++i], 10);
32
+ else if (a === "--host") args.host = argv[++i];
33
+ else if (a === "--dir") args.dir = argv[++i];
34
+ else if (a === "--no-browser") args.noBrowser = true;
35
+ else if (a === "--llm") args.llm = parseInt(argv[++i], 10);
36
+ else if (a === "--open") args.noBrowser = false;
37
+ }
38
+ return args;
39
+ }
40
+
41
+ function showHelp() {
42
+ console.log(`
43
+ ${c("bold", "cybercode")} ${c("dim", `v${pkg.version}`)} — Codex-dark web UI with a built-in self-evolving agent
44
+
45
+ ${c("bold", "USAGE")}
46
+ npx cybercode # start with defaults
47
+ npx cybercode --port 8080 # custom port
48
+ npx cybercode --host 0.0.0.0 # listen on all interfaces
49
+ npx cybercode --no-browser # don't auto-open browser
50
+ npx cybercode --dir ~/my-agent # custom working directory
51
+ npx cybercode --llm 1 # start on 2nd configured LLM
52
+
53
+ ${c("bold", "OPTIONS")}
54
+ -p, --port <num> Port (default: auto-find free port near 18600)
55
+ --host <addr> Bind address (default: 127.0.0.1)
56
+ --dir <path> Working directory (default: ~/.cybercode)
57
+ --llm <num> LLM index to start with (default: 0)
58
+ --no-browser Don't auto-open the browser
59
+ -h, --help Show this help
60
+ -V, --version Show version
61
+
62
+ ${c("bold", "FIRST RUN")}
63
+ On first launch, a ${c("cyan", "mykey.json")} template is created in the working
64
+ directory. Edit it with your LLM API keys (OpenAI, DeepSeek, etc.), then
65
+ restart. Any OpenAI-compatible endpoint works.
66
+
67
+ ${c("bold", "ATTRIBUTION")}
68
+ Agent core inspired by GenericAgent (MIT). HyperFrames skills from HeyGen (MIT).
69
+ See LICENSE for details.
70
+ `);
71
+ }
72
+
73
+ // ---- Find Python 3.11+ ----
74
+ function findPython() {
75
+ const candidates = process.env.WEBUI_CODEX_PYTHON
76
+ ? [process.env.WEBUI_CODEX_PYTHON]
77
+ : ["python3", "python3.12", "python3.11", "python"];
78
+
79
+ for (const cmd of candidates) {
80
+ try {
81
+ const result = spawnSync(cmd, ["--version"], { encoding: "utf-8", timeout: 5000 });
82
+ const output = (result.stdout || "") + (result.stderr || "");
83
+ const match = output.match(/Python (\d+)\.(\d+)/);
84
+ if (match) {
85
+ const major = parseInt(match[1], 10);
86
+ const minor = parseInt(match[2], 10);
87
+ if (major > 3 || (major === 3 && minor >= 11)) {
88
+ return cmd;
89
+ }
90
+ }
91
+ } catch {}
92
+ }
93
+
94
+ console.error(c("red", "✗ Python 3.11+ not found."));
95
+ console.error(c("dim", " Install Python 3.11 or 3.12, or set WEBUI_CODEX_PYTHON env var."));
96
+ console.error(c("dim", " Download: https://www.python.org/downloads/"));
97
+ process.exit(1);
98
+ }
99
+
100
+ // ---- Find a free port ----
101
+ function findFreePort(preferred) {
102
+ const start = preferred || 18600;
103
+ for (let port = start; port <= start + 100; port++) {
104
+ try {
105
+ const result = spawnSync("python3", ["-c", `import socket; s=socket.socket(); s.bind(("127.0.0.1",${port})); s.close(); print(${port})`], { encoding: "utf-8", timeout: 3000 });
106
+ if (result.stdout && result.stdout.trim()) return parseInt(result.stdout.trim(), 10);
107
+ } catch {}
108
+ }
109
+ return start;
110
+ }
111
+
112
+ // ---- Copy directory recursively ----
113
+ function copyDir(src, dest) {
114
+ if (!existsSync(dest)) mkdirSync(dest, { recursive: true });
115
+ for (const entry of readdirSync(src)) {
116
+ const srcPath = join(src, entry);
117
+ const destPath = join(dest, entry);
118
+ const stat = statSync(srcPath);
119
+ if (stat.isDirectory()) {
120
+ copyDir(srcPath, destPath);
121
+ } else {
122
+ copyFileSync(srcPath, destPath);
123
+ }
124
+ }
125
+ }
126
+
127
+ // ---- Ensure requests is installed ----
128
+ function ensureRequests(python) {
129
+ try {
130
+ const result = spawnSync(python, ["-c", "import requests; print(requests.__version__)"], { encoding: "utf-8", timeout: 5000 });
131
+ if (result.status === 0 && result.stdout.trim()) {
132
+ return; // already installed
133
+ }
134
+ } catch {}
135
+
136
+ console.log(c("yellow", "→ installing requests..."));
137
+ try {
138
+ spawnSync(python, ["-m", "pip", "install", "requests", "--quiet", "--disable-pip-version-check"], { stdio: "inherit", timeout: 60000 });
139
+ } catch {
140
+ console.error(c("red", "✗ Failed to install requests. Please run: pip install requests"));
141
+ process.exit(1);
142
+ }
143
+ }
144
+
145
+ // ---- Wait for server ----
146
+ function waitForServer(url, maxRetries = 30) {
147
+ return new Promise((resolve, reject) => {
148
+ let retries = 0;
149
+ const check = () => {
150
+ import("node:http").then(({ default: http }) => {
151
+ const req = http.get(url, (res) => {
152
+ res.resume();
153
+ if (res.statusCode === 200) resolve();
154
+ else if (++retries < maxRetries) setTimeout(check, 300);
155
+ else reject(new Error("server unhealthy"));
156
+ });
157
+ req.on("error", () => {
158
+ if (++retries < maxRetries) setTimeout(check, 300);
159
+ else reject(new Error("server not responding"));
160
+ });
161
+ req.setTimeout(2000, () => { req.destroy(); if (++retries < maxRetries) setTimeout(check, 300); else reject(new Error("timeout")); });
162
+ });
163
+ };
164
+ check();
165
+ });
166
+ }
167
+
168
+ // ---- Open browser ----
169
+ function openBrowser(url) {
170
+ const cmds = {
171
+ darwin: ["open", [url]],
172
+ win32: ["cmd", ["/c", "start", url]],
173
+ linux: ["xdg-open", [url]],
174
+ };
175
+ const plat = platform();
176
+ const entry = cmds[plat] || cmds.linux;
177
+ try {
178
+ spawn(entry[0], entry[1], { detached: true, stdio: "ignore" }).unref();
179
+ } catch {}
180
+ }
181
+
182
+ // ---- Main ----
183
+ async function main() {
184
+ const args = parseArgs();
185
+
186
+ if (args.help) { showHelp(); process.exit(0); }
187
+ if (args.version) { console.log(pkg.version); process.exit(0); }
188
+
189
+ const python = findPython();
190
+ const workDir = resolve(args.dir || join(homedir(), ".cybercode"));
191
+
192
+ // Create working directory
193
+ if (!existsSync(workDir)) mkdirSync(workDir, { recursive: true });
194
+
195
+ // Version stamp — re-copy files if package version changed
196
+ const stampPath = join(workDir, ".version");
197
+ const currentVersion = pkg.version;
198
+ let needsCopy = true;
199
+ if (existsSync(stampPath)) {
200
+ const stamped = readFileSync(stampPath, "utf-8").trim();
201
+ if (stamped === currentVersion) needsCopy = false;
202
+ }
203
+
204
+ // Copy bundled files
205
+ const bundledPython = join(__dirname, "..", "python");
206
+ const bundledSkills = join(__dirname, "..", "skills");
207
+
208
+ if (needsCopy) {
209
+ if (existsSync(bundledPython)) copyDir(bundledPython, workDir);
210
+ if (existsSync(bundledSkills)) copyDir(bundledSkills, join(workDir, "skills"));
211
+ writeFileSync(stampPath, currentVersion, "utf-8");
212
+ }
213
+
214
+ // Create mykey.json from template if not exists
215
+ const mykeyPath = join(workDir, "mykey.json");
216
+ if (!existsSync(mykeyPath)) {
217
+ const templatePath = join(__dirname, "..", "templates", "mykey_template.json");
218
+ if (existsSync(templatePath)) {
219
+ copyFileSync(templatePath, mykeyPath);
220
+ } else {
221
+ writeFileSync(mykeyPath, JSON.stringify({
222
+ llm1: { apikey: "sk-YOUR-KEY", apibase: "https://api.openai.com", model: "gpt-4o", name: "GPT-4o" }
223
+ }, null, 2));
224
+ }
225
+ }
226
+
227
+ // Check if mykey.json has been configured
228
+ let configured = false;
229
+ try {
230
+ const mykey = JSON.parse(readFileSync(mykeyPath, "utf-8"));
231
+ configured = Object.values(mykey).some(v => v.apikey && !v.apikey.includes("YOUR-"));
232
+ } catch {}
233
+
234
+ // Ensure requests
235
+ ensureRequests(python);
236
+
237
+ // Find port
238
+ const port = args.port || findFreePort(18600);
239
+ const url = `http://${args.host}:${port}`;
240
+
241
+ // Banner
242
+ console.log();
243
+ console.log(` ${c("bold", c("blue", "╭─────────────────────────────────────────────────╮"))}`);
244
+ console.log(` ${c("bold", c("blue", "│"))} ${c("bold", "cybercode")} ${c("dim", `v${currentVersion}`)} ${c("bold", c("blue", "│"))}`);
245
+ console.log(` ${c("bold", c("blue", "│"))} ${c("dim", "working dir:")} ${workDir.padEnd(34).slice(0, 34)} ${c("bold", c("blue", "│"))}`);
246
+ console.log(` ${c("bold", c("blue", "│"))} ${c("green", `▶ ${url}`)}${" ".repeat(Math.max(0, 33 - url.length))} ${c("bold", c("blue", "│"))}`);
247
+ if (!configured) {
248
+ console.log(` ${c("bold", c("blue", "│"))} ${c("yellow", "⚠ edit mykey.json to add your API key")} ${c("bold", c("blue", "│"))}`);
249
+ }
250
+ console.log(` ${c("bold", c("blue", "╰─────────────────────────────────────────────────╯"))}`);
251
+ console.log();
252
+
253
+ if (!configured) {
254
+ console.log(c("yellow", ` ⚠ mykey.json not configured yet.`));
255
+ console.log(c("dim", ` Edit: ${mykeyPath}`));
256
+ console.log(c("dim", ` Add your OpenAI-compatible API key, then restart.`));
257
+ console.log(c("dim", ` Or run: nano ${mykeyPath}`));
258
+ console.log(c("dim", ` The UI will still load and show a setup banner.`));
259
+ console.log();
260
+ }
261
+
262
+ // Launch Python server
263
+ const pyArgs = [
264
+ join(workDir, "webui_codex.py"),
265
+ "--port", String(port),
266
+ "--host", args.host,
267
+ "--llm_no", String(args.llm),
268
+ ];
269
+
270
+ const child = spawn(python, pyArgs, {
271
+ cwd: workDir,
272
+ stdio: ["ignore", "pipe", "pipe"],
273
+ env: { ...process.env, PYTHONUNBUFFERED: "1" },
274
+ });
275
+
276
+ // Pipe server output
277
+ child.stdout.on("data", (data) => process.stdout.write(data));
278
+ child.stderr.on("data", (data) => process.stderr.write(c("dim", data.toString())));
279
+
280
+ child.on("error", (err) => {
281
+ console.error(c("red", `✗ Failed to start: ${err.message}`));
282
+ process.exit(1);
283
+ });
284
+
285
+ child.on("exit", (code) => {
286
+ process.exit(code || 0);
287
+ });
288
+
289
+ // Wait for server, then open browser
290
+ if (!args.noBrowser) {
291
+ try {
292
+ await waitForServer(`${url}/api/status`, 40);
293
+ openBrowser(url);
294
+ } catch {
295
+ // Server might still be starting; the user can open manually
296
+ console.log(c("dim", ` (browser auto-open skipped — open ${url} manually)`));
297
+ }
298
+ }
299
+
300
+ // Handle Ctrl+C
301
+ process.on("SIGINT", () => {
302
+ child.kill("SIGINT");
303
+ process.exit(0);
304
+ });
305
+ process.on("SIGTERM", () => {
306
+ child.kill("SIGTERM");
307
+ process.exit(0);
308
+ });
309
+ }
310
+
311
+ main().catch((err) => {
312
+ console.error(c("red", `✗ ${err.message}`));
313
+ process.exit(1);
314
+ });
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "cybercode-cli",
3
+ "version": "1.0.0",
4
+ "description": "Codex-dark web UI with a built-in self-evolving agent + HyperFrames video skills",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "type": "module",
9
+ "bin": {
10
+ "cybercode": "bin/cli.mjs"
11
+ },
12
+ "files": [
13
+ "bin/",
14
+ "python/",
15
+ "skills/",
16
+ "templates/",
17
+ "LICENSE",
18
+ "README.md"
19
+ ],
20
+ "engines": {
21
+ "node": ">=18"
22
+ },
23
+ "keywords": [
24
+ "agent",
25
+ "ai",
26
+ "llm",
27
+ "codex",
28
+ "web-ui",
29
+ "automation",
30
+ "self-evolving",
31
+ "hyperframes",
32
+ "video-generation"
33
+ ],
34
+ "license": "MIT",
35
+ "repository": {
36
+ "type": "git",
37
+ "url": ""
38
+ },
39
+ "homepage": "",
40
+ "scripts": {
41
+ "start": "node bin/cli.mjs",
42
+ "prepublishOnly": "node -e \"console.log('Publishing cybercode...')\""
43
+ }
44
+ }