cybercode-cli 1.0.0 → 1.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/bin/cli.mjs CHANGED
@@ -2,7 +2,7 @@
2
2
  // cybercode CLI launcher — bootstraps a Python env and starts the web UI.
3
3
  // Designed for `npx cybercode` one-click usage.
4
4
 
5
- import { spawn, spawnSync, execSync } from "node:child_process";
5
+ import { spawn, spawnSync } from "node:child_process";
6
6
  import { existsSync, mkdirSync, copyFileSync, readdirSync, statSync, writeFileSync, readFileSync } from "node:fs";
7
7
  import { join, dirname, resolve } from "node:path";
8
8
  import { homedir, platform } from "node:os";
@@ -20,10 +20,15 @@ const COLORS = {
20
20
  };
21
21
  const c = (color, text) => `${COLORS[color] || ""}${text}${COLORS.reset}`;
22
22
 
23
+ function splitCommand(argv) {
24
+ if (argv[0] === "webui") return { command: "webui", args: argv.slice(1) };
25
+ return { command: "webui", args: argv };
26
+ }
27
+
23
28
  // ---- Parse CLI args ----
24
- function parseArgs() {
29
+ function parseArgs(rawArgv) {
25
30
  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);
31
+ const argv = rawArgv.slice();
27
32
  for (let i = 0; i < argv.length; i++) {
28
33
  const a = argv[i];
29
34
  if (a === "-h" || a === "--help") args.help = true;
@@ -43,12 +48,12 @@ function showHelp() {
43
48
  ${c("bold", "cybercode")} ${c("dim", `v${pkg.version}`)} — Codex-dark web UI with a built-in self-evolving agent
44
49
 
45
50
  ${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
51
+ npx cybercode webui # start the web UI
52
+ npx cybercode webui --port 8080 # custom port
53
+ npx cybercode webui --host 0.0.0.0 # listen on all interfaces
54
+ npx cybercode webui --no-browser # don't auto-open browser
55
+ npx cybercode webui --dir ~/my-agent # custom working directory
56
+ npx cybercode webui --llm 1 # start on 2nd configured LLM
52
57
 
53
58
  ${c("bold", "OPTIONS")}
54
59
  -p, --port <num> Port (default: auto-find free port near 18600)
@@ -84,9 +89,7 @@ function findPython() {
84
89
  if (match) {
85
90
  const major = parseInt(match[1], 10);
86
91
  const minor = parseInt(match[2], 10);
87
- if (major > 3 || (major === 3 && minor >= 11)) {
88
- return cmd;
89
- }
92
+ if (major > 3 || (major === 3 && minor >= 11)) return cmd;
90
93
  }
91
94
  } catch {}
92
95
  }
@@ -109,40 +112,30 @@ function findFreePort(preferred) {
109
112
  return start;
110
113
  }
111
114
 
112
- // ---- Copy directory recursively ----
113
115
  function copyDir(src, dest) {
114
116
  if (!existsSync(dest)) mkdirSync(dest, { recursive: true });
115
117
  for (const entry of readdirSync(src)) {
116
118
  const srcPath = join(src, entry);
117
119
  const destPath = join(dest, entry);
118
120
  const stat = statSync(srcPath);
119
- if (stat.isDirectory()) {
120
- copyDir(srcPath, destPath);
121
- } else {
122
- copyFileSync(srcPath, destPath);
123
- }
121
+ if (stat.isDirectory()) copyDir(srcPath, destPath);
122
+ else copyFileSync(srcPath, destPath);
124
123
  }
125
124
  }
126
125
 
127
- // ---- Ensure requests is installed ----
128
126
  function ensureRequests(python) {
129
127
  try {
130
128
  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
- }
129
+ if (result.status === 0 && result.stdout.trim()) return;
134
130
  } catch {}
135
-
136
131
  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 {
132
+ const install = spawnSync(python, ["-m", "pip", "install", "requests", "--quiet", "--disable-pip-version-check"], { stdio: "inherit", timeout: 60000 });
133
+ if (install.status !== 0) {
140
134
  console.error(c("red", "✗ Failed to install requests. Please run: pip install requests"));
141
135
  process.exit(1);
142
136
  }
143
137
  }
144
138
 
145
- // ---- Wait for server ----
146
139
  function waitForServer(url, maxRetries = 30) {
147
140
  return new Promise((resolve, reject) => {
148
141
  let retries = 0;
@@ -165,7 +158,6 @@ function waitForServer(url, maxRetries = 30) {
165
158
  });
166
159
  }
167
160
 
168
- // ---- Open browser ----
169
161
  function openBrowser(url) {
170
162
  const cmds = {
171
163
  darwin: ["open", [url]],
@@ -174,25 +166,18 @@ function openBrowser(url) {
174
166
  };
175
167
  const plat = platform();
176
168
  const entry = cmds[plat] || cmds.linux;
177
- try {
178
- spawn(entry[0], entry[1], { detached: true, stdio: "ignore" }).unref();
179
- } catch {}
169
+ try { spawn(entry[0], entry[1], { detached: true, stdio: "ignore" }).unref(); } catch {}
180
170
  }
181
171
 
182
- // ---- Main ----
183
- async function main() {
184
- const args = parseArgs();
185
-
172
+ async function launchWebUI(rawArgv) {
173
+ const args = parseArgs(rawArgv);
186
174
  if (args.help) { showHelp(); process.exit(0); }
187
175
  if (args.version) { console.log(pkg.version); process.exit(0); }
188
176
 
189
177
  const python = findPython();
190
178
  const workDir = resolve(args.dir || join(homedir(), ".cybercode"));
191
-
192
- // Create working directory
193
179
  if (!existsSync(workDir)) mkdirSync(workDir, { recursive: true });
194
180
 
195
- // Version stamp — re-copy files if package version changed
196
181
  const stampPath = join(workDir, ".version");
197
182
  const currentVersion = pkg.version;
198
183
  let needsCopy = true;
@@ -201,52 +186,36 @@ async function main() {
201
186
  if (stamped === currentVersion) needsCopy = false;
202
187
  }
203
188
 
204
- // Copy bundled files
205
189
  const bundledPython = join(__dirname, "..", "python");
206
190
  const bundledSkills = join(__dirname, "..", "skills");
207
-
208
191
  if (needsCopy) {
209
192
  if (existsSync(bundledPython)) copyDir(bundledPython, workDir);
210
193
  if (existsSync(bundledSkills)) copyDir(bundledSkills, join(workDir, "skills"));
211
194
  writeFileSync(stampPath, currentVersion, "utf-8");
212
195
  }
213
196
 
214
- // Create mykey.json from template if not exists
215
197
  const mykeyPath = join(workDir, "mykey.json");
216
198
  if (!existsSync(mykeyPath)) {
217
199
  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
- }
200
+ if (existsSync(templatePath)) copyFileSync(templatePath, mykeyPath);
225
201
  }
226
202
 
227
- // Check if mykey.json has been configured
228
203
  let configured = false;
229
204
  try {
230
205
  const mykey = JSON.parse(readFileSync(mykeyPath, "utf-8"));
231
- configured = Object.values(mykey).some(v => v.apikey && !v.apikey.includes("YOUR-"));
206
+ configured = Object.values(mykey).some(v => v.apikey && !String(v.apikey).includes("YOUR-"));
232
207
  } catch {}
233
208
 
234
- // Ensure requests
235
209
  ensureRequests(python);
236
-
237
- // Find port
238
210
  const port = args.port || findFreePort(18600);
239
211
  const url = `http://${args.host}:${port}`;
240
212
 
241
- // Banner
242
213
  console.log();
243
214
  console.log(` ${c("bold", c("blue", "╭─────────────────────────────────────────────────╮"))}`);
244
215
  console.log(` ${c("bold", c("blue", "│"))} ${c("bold", "cybercode")} ${c("dim", `v${currentVersion}`)} ${c("bold", c("blue", "│"))}`);
245
216
  console.log(` ${c("bold", c("blue", "│"))} ${c("dim", "working dir:")} ${workDir.padEnd(34).slice(0, 34)} ${c("bold", c("blue", "│"))}`);
246
217
  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
- }
218
+ if (!configured) console.log(` ${c("bold", c("blue", "│"))} ${c("yellow", "⚠ edit mykey.json to add your API key")} ${c("bold", c("blue", "│"))}`);
250
219
  console.log(` ${c("bold", c("blue", "╰─────────────────────────────────────────────────╯"))}`);
251
220
  console.log();
252
221
 
@@ -254,61 +223,30 @@ async function main() {
254
223
  console.log(c("yellow", ` ⚠ mykey.json not configured yet.`));
255
224
  console.log(c("dim", ` Edit: ${mykeyPath}`));
256
225
  console.log(c("dim", ` Add your OpenAI-compatible API key, then restart.`));
257
- console.log(c("dim", ` Or run: nano ${mykeyPath}`));
258
226
  console.log(c("dim", ` The UI will still load and show a setup banner.`));
259
227
  console.log();
260
228
  }
261
229
 
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
230
+ const pyArgs = [join(workDir, "webui_codex.py"), "--port", String(port), "--host", args.host, "--llm_no", String(args.llm)];
231
+ const child = spawn(python, pyArgs, { cwd: workDir, stdio: ["ignore", "pipe", "pipe"], env: { ...process.env, PYTHONUNBUFFERED: "1" } });
277
232
  child.stdout.on("data", (data) => process.stdout.write(data));
278
233
  child.stderr.on("data", (data) => process.stderr.write(c("dim", data.toString())));
234
+ child.on("error", (err) => { console.error(c("red", `✗ Failed to start: ${err.message}`)); process.exit(1); });
235
+ child.on("exit", (code) => process.exit(code || 0));
279
236
 
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
237
  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
- }
238
+ try { await waitForServer(`${url}/api/status`, 40); openBrowser(url); }
239
+ catch { console.log(c("dim", ` (browser auto-open skipped — open ${url} manually)`)); }
298
240
  }
299
241
 
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
- });
242
+ process.on("SIGINT", () => { child.kill("SIGINT"); process.exit(0); });
243
+ process.on("SIGTERM", () => { child.kill("SIGTERM"); process.exit(0); });
309
244
  }
310
245
 
311
- main().catch((err) => {
312
- console.error(c("red", `✗ ${err.message}`));
313
- process.exit(1);
314
- });
246
+ const argv = process.argv.slice(2);
247
+ const { command, args } = splitCommand(argv);
248
+ if (command === "webui") {
249
+ launchWebUI(args).catch((err) => { console.error(c("red", `✗ ${err.message}`)); process.exit(1); });
250
+ } else {
251
+ showHelp();
252
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cybercode-cli",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Codex-dark web UI with a built-in self-evolving agent + HyperFrames video skills",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -567,7 +567,7 @@ def main():
567
567
 
568
568
  url = f"http://{args.host}:{args.port}"
569
569
  print(f"\n ╭───────────────────────────────────────────╮")
570
- print(f" │ webui_codex · self-contained agent │")
570
+ print(f" │ cybercode · self-contained agent │")
571
571
  print(f" │ open {url:<33}│")
572
572
  print(f" │ Ctrl+C to stop │")
573
573
  print(f" ╰───────────────────────────────────────────╯\n")