@meyverick/omnicode 0.0.5 → 0.0.6

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 CHANGED
@@ -4,7 +4,7 @@ The cross-platform command-line entrypoint for running OpenCode through OmniRout
4
4
 
5
5
  ## What is omnicode?
6
6
 
7
- `omnicode` is a thin wrapper that launches OpenCode through OmniRoute. It expects you to install the underlying tools yourself, then handles optional GrayMatter and OpenSpec initialization, background OmniRoute lifecycle, and project-local OpenCode sessions. Developed and tested on Ubuntu Linux cross-platform by design but untested on Windows, macOS, and other Linux distributions.
7
+ `omnicode` is a thin wrapper that launches OpenCode through OmniRoute. It expects you to install the underlying tools yourself, then handles optional GrayMatter and OpenSpec initialization, background OmniRoute lifecycle, and project-local OpenCode sessions. Developed and tested on Ubuntu Linux; cross-platform by design but untested on Windows, macOS, and other Linux distributions.
8
8
 
9
9
  ## Why
10
10
 
@@ -17,7 +17,7 @@ So I wrote this wrapper. It starts OmniRoute, inits GrayMatter and OpenSpec quie
17
17
  ## Features
18
18
 
19
19
  - Thin npm global command written in Node.js (no bash needed).
20
- - Cross-platform by design developed and tested on Ubuntu Linux; untested on Windows, macOS, and other Linux distributions.
20
+ - Cross-platform by design: developed and tested on Ubuntu Linux; untested on Windows, macOS, and other Linux distributions.
21
21
  - Automatic session resume per project using the OpenCode database.
22
22
  - Background OmniRoute lifecycle with cleanup when OpenCode exits.
23
23
  - Quiet GrayMatter and OpenSpec initialization with captured logs.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meyverick/omnicode",
3
- "version": "0.0.5",
3
+ "version": "0.0.6",
4
4
  "description": "Ubuntu command-line entrypoint for OpenCode through OmniRoute",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,66 +1,45 @@
1
- import { spawn, execFileSync } from "node:child_process";
2
- import { existsSync, mkdirSync, openSync, readFileSync, writeFileSync, unlinkSync } from "node:fs";
1
+ import { spawn } from "node:child_process";
2
+ import { closeSync, existsSync, mkdirSync, openSync, readFileSync, writeFileSync, unlinkSync } from "node:fs";
3
3
  import { join } from "node:path";
4
- import os from "node:os";
5
4
 
6
- import { commandExists, getDataDir } from "../installer/lib.js";
5
+ import { commandExists, getDataDir, isProcessRunning, isPidAlive } from "../installer/lib.js";
7
6
 
8
- const isWindows = process.platform === "win32";
9
7
  const MAX_OMNI_WAIT = 30;
10
8
  const OMNI_CHECK_DELAY = 1000;
11
9
 
12
- function isProcessRunning(name) {
13
- try {
14
- if (isWindows) {
15
- const out = execFileSync("tasklist", ["/FI", `IMAGENAME eq ${name}.exe`, "/NH"], {
16
- stdio: ["ignore", "pipe", "ignore"],
17
- encoding: "utf8",
18
- });
19
- return out.includes(`${name}.exe`);
20
- }
21
- execFileSync("pgrep", ["-x", name], { stdio: "ignore" });
22
- return true;
23
- } catch {
24
- return false;
25
- }
26
- }
27
-
28
- function isPidAlive(pid) {
29
- try {
30
- process.kill(pid, 0);
31
- return true;
32
- } catch {
33
- return false;
34
- }
35
- }
36
-
37
10
  function sleep(ms) {
38
11
  return new Promise((resolve) => setTimeout(resolve, ms));
39
12
  }
40
13
 
41
- function initTool(name, args, logPath) {
42
- return new Promise((resolve) => {
43
- if (!commandExists(name)) {
44
- console.log(`[omnicode] ${name}: not installed, skipping`);
45
- resolve();
46
- return;
47
- }
48
- console.log(`[omnicode] ${name}: initializing`);
49
- const log = openSync(logPath, "w");
14
+ async function initTool(name, args, logPath) {
15
+ if (!commandExists(name)) {
16
+ console.log(`[omnicode] ${name}: not installed, skipping`);
17
+ return;
18
+ }
19
+ console.log(`[omnicode] ${name}: initializing`);
20
+ let log;
21
+ try {
22
+ log = openSync(logPath, "w");
50
23
  const child = spawn(name, args, { stdio: ["ignore", log, log] });
51
- child.on("close", (code) => {
52
- if (code === 0) {
53
- console.log(`[omnicode] ${name}: ready`);
54
- } else {
55
- console.log(`[omnicode] WARNING: ${name} init failed; continuing. Log: ${logPath}`);
56
- }
57
- resolve();
58
- });
59
- child.on("error", () => {
24
+ const result = await Promise.race([
25
+ new Promise((resolve) => {
26
+ child.on("close", resolve);
27
+ child.on("error", () => resolve(null));
28
+ }),
29
+ new Promise((resolve) => {
30
+ setTimeout(() => { child.kill(); resolve(null); }, 30000);
31
+ }),
32
+ ]);
33
+ if (result === null) {
34
+ console.log(`[omnicode] WARNING: ${name} init timed out; continuing. Log: ${logPath}`);
35
+ } else if (result === 0) {
36
+ console.log(`[omnicode] ${name}: ready`);
37
+ } else {
60
38
  console.log(`[omnicode] WARNING: ${name} init failed; continuing. Log: ${logPath}`);
61
- resolve();
62
- });
63
- });
39
+ }
40
+ } catch {
41
+ if (log !== undefined) try { closeSync(log); } catch {}
42
+ }
64
43
  }
65
44
 
66
45
  async function initTools(dataDir) {
@@ -137,9 +116,13 @@ function stopOmnirouteIfIdle(pidFile) {
137
116
  } catch {}
138
117
  if (pid && isPidAlive(pid)) {
139
118
  console.log(`[omnicode] no opencode left -> stopping omniroute (pid: ${pid})`);
140
- try { process.kill(pid, "SIGTERM"); } catch {}
141
- const deadline = Date.now() + 5000;
142
- while (isPidAlive(pid) && Date.now() < deadline) {
119
+ if (process.platform === "win32") {
120
+ spawn("taskkill", ["/T", "/F", "/PID", String(pid)], { stdio: "ignore" });
121
+ } else {
122
+ try { process.kill(-pid, "SIGTERM"); } catch {}
123
+ }
124
+ for (let i = 0; i < 10; i++) {
125
+ if (!isPidAlive(pid)) break;
143
126
  try { process.kill(pid, 0); } catch { break; }
144
127
  }
145
128
  }
@@ -149,7 +132,7 @@ function stopOmnirouteIfIdle(pidFile) {
149
132
 
150
133
  export async function runRuntime(mode) {
151
134
  const dataDir = getDataDir();
152
- mkdirSync(dataDir, { recursive: true });
135
+ mkdirSync(dataDir, { recursive: true, mode: 0o700 });
153
136
 
154
137
  const logFile = join(dataDir, "omniroute.log");
155
138
  const pidFile = join(dataDir, "omniroute.pid");
@@ -163,12 +146,12 @@ export async function runRuntime(mode) {
163
146
  process.on("SIGINT", () => { cleanup(); process.exit(0); });
164
147
  process.on("SIGTERM", () => { cleanup(); process.exit(0); });
165
148
 
166
- await initTools(dataDir);
167
-
168
149
  const pid = startOmniroute(dataDir, logFile, pidFile);
169
- if (pid !== null) {
170
- await waitForOmniroute(pid, logFile);
171
- }
150
+
151
+ await Promise.all([
152
+ pid !== null ? waitForOmniroute(pid, logFile) : Promise.resolve(),
153
+ initTools(dataDir),
154
+ ]);
172
155
 
173
156
  if (mode.flag === "-s" && mode.id) {
174
157
  console.log(`[omnicode] launching opencode (session: ${mode.id})`);
@@ -1,17 +1,15 @@
1
1
  #!/usr/bin/env node
2
- import { execFileSync } from "node:child_process";
3
2
  import { readFileSync, realpathSync } from "node:fs";
4
3
  import { join } from "node:path";
5
4
  import { fileURLToPath } from "node:url";
6
5
 
7
- import { commandExists, getOpencodeDbPath } from "../installer/lib.js";
6
+ import { commandExists, getOpencodeDbPath, isProcessRunning } from "../installer/lib.js";
8
7
  import { runRuntime } from "./omnicode-runtime.js";
9
8
 
10
9
  const __dirname = fileURLToPath(new URL(".", import.meta.url));
11
10
  const packageJsonPath = join(__dirname, "..", "..", "package.json");
12
11
 
13
- const SESSION_ID_RE = /^[a-zA-Z0-9_-]+$/;
14
- const isWindows = process.platform === "win32";
12
+ const SESSION_ID_RE = /^(?=.{1,128}$)[a-zA-Z0-9_-]+$/;
15
13
 
16
14
  let DatabaseSync;
17
15
  try {
@@ -33,22 +31,6 @@ export function getVersion() {
33
31
  return _cachedVersion;
34
32
  }
35
33
 
36
- export function isProcessRunning(name) {
37
- try {
38
- if (isWindows) {
39
- const out = execFileSync("tasklist", ["/FI", `IMAGENAME eq ${name}.exe`, "/NH"], {
40
- stdio: ["ignore", "pipe", "ignore"],
41
- encoding: "utf8",
42
- });
43
- return out.includes(`${name}.exe`);
44
- }
45
- execFileSync("pgrep", ["-x", name], { stdio: "ignore" });
46
- return true;
47
- } catch {
48
- return false;
49
- }
50
- }
51
-
52
34
  export function getProcessStatus() {
53
35
  return {
54
36
  opencode: isProcessRunning("opencode"),
@@ -15,6 +15,35 @@ export function commandExists(command) {
15
15
  }
16
16
  }
17
17
 
18
+ export function isProcessRunning(name) {
19
+ try {
20
+ if (isWindows) {
21
+ const extensions = [".exe", ".cmd", ".bat"];
22
+ for (const ext of extensions) {
23
+ const out = execFileSync("tasklist", ["/FI", `IMAGENAME eq ${name}${ext}`, "/NH"], {
24
+ stdio: ["ignore", "pipe", "ignore"],
25
+ encoding: "utf8",
26
+ });
27
+ if (out.includes(`${name}${ext}`)) return true;
28
+ }
29
+ return false;
30
+ }
31
+ execFileSync("pgrep", ["-x", name], { stdio: "ignore" });
32
+ return true;
33
+ } catch {
34
+ return false;
35
+ }
36
+ }
37
+
38
+ export function isPidAlive(pid) {
39
+ try {
40
+ process.kill(pid, 0);
41
+ return true;
42
+ } catch {
43
+ return false;
44
+ }
45
+ }
46
+
18
47
  export function getDataDir() {
19
48
  return join(os.homedir(), ".local", "share", "omnicode");
20
49
  }