@meyverick/omnicode 0.0.4 → 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 +4 -3
- package/package.json +1 -1
- package/src/bin/omnicode-runtime.js +175 -0
- package/src/bin/omnicode.js +23 -46
- package/src/installer/lib.js +40 -25
- package/src/bin/omnicode-runtime.sh +0 -121
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# omnicode
|
|
2
2
|
|
|
3
|
-
The
|
|
3
|
+
The cross-platform command-line entrypoint for running OpenCode through OmniRoute.
|
|
4
4
|
|
|
5
5
|
## What is omnicode?
|
|
6
6
|
|
|
7
|
-
`omnicode` is a thin wrapper that launches OpenCode through OmniRoute
|
|
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
|
|
|
@@ -16,7 +16,8 @@ So I wrote this wrapper. It starts OmniRoute, inits GrayMatter and OpenSpec quie
|
|
|
16
16
|
|
|
17
17
|
## Features
|
|
18
18
|
|
|
19
|
-
- Thin npm global command
|
|
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
21
|
- Automatic session resume per project using the OpenCode database.
|
|
21
22
|
- Background OmniRoute lifecycle with cleanup when OpenCode exits.
|
|
22
23
|
- Quiet GrayMatter and OpenSpec initialization with captured logs.
|
package/package.json
CHANGED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { closeSync, existsSync, mkdirSync, openSync, readFileSync, writeFileSync, unlinkSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
|
|
5
|
+
import { commandExists, getDataDir, isProcessRunning, isPidAlive } from "../installer/lib.js";
|
|
6
|
+
|
|
7
|
+
const MAX_OMNI_WAIT = 30;
|
|
8
|
+
const OMNI_CHECK_DELAY = 1000;
|
|
9
|
+
|
|
10
|
+
function sleep(ms) {
|
|
11
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
12
|
+
}
|
|
13
|
+
|
|
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");
|
|
23
|
+
const child = spawn(name, args, { stdio: ["ignore", log, log] });
|
|
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 {
|
|
38
|
+
console.log(`[omnicode] WARNING: ${name} init failed; continuing. Log: ${logPath}`);
|
|
39
|
+
}
|
|
40
|
+
} catch {
|
|
41
|
+
if (log !== undefined) try { closeSync(log); } catch {}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function initTools(dataDir) {
|
|
46
|
+
const graymatterLog = join(dataDir, "graymatter-init.log");
|
|
47
|
+
const openspecLog = join(dataDir, "openspec-init.log");
|
|
48
|
+
|
|
49
|
+
await Promise.all([
|
|
50
|
+
initTool("graymatter", ["init", "--only", "opencode"], graymatterLog),
|
|
51
|
+
initTool("openspec", ["init", "--force", "--tools", "opencode"], openspecLog),
|
|
52
|
+
]);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function startOmniroute(dataDir, logFile, pidFile) {
|
|
56
|
+
if (isProcessRunning("omniroute")) {
|
|
57
|
+
console.log("[omnicode] omniroute already running");
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (existsSync(pidFile) || existsSync(pidFile)) {
|
|
62
|
+
let pid = null;
|
|
63
|
+
try {
|
|
64
|
+
pid = parseInt(readFileSync(pidFile, "utf8").trim(), 10);
|
|
65
|
+
} catch {}
|
|
66
|
+
if (pid && isPidAlive(pid)) {
|
|
67
|
+
console.log(`[omnicode] omniroute already running (pid: ${pid})`);
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
try { unlinkSync(pidFile); } catch {}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
console.log("[omnicode] starting omniroute...");
|
|
74
|
+
const log = openSync(logFile, "w");
|
|
75
|
+
const child = spawn("omniroute", ["--no-open"], {
|
|
76
|
+
detached: true,
|
|
77
|
+
stdio: ["ignore", log, log],
|
|
78
|
+
});
|
|
79
|
+
child.unref();
|
|
80
|
+
const pid = child.pid;
|
|
81
|
+
writeFileSync(pidFile, String(pid), { mode: 0o600 });
|
|
82
|
+
|
|
83
|
+
return pid;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function waitForOmniroute(pid, logFile) {
|
|
87
|
+
let waited = 0;
|
|
88
|
+
while (waited < MAX_OMNI_WAIT * 1000) {
|
|
89
|
+
await sleep(OMNI_CHECK_DELAY);
|
|
90
|
+
waited += OMNI_CHECK_DELAY;
|
|
91
|
+
|
|
92
|
+
if (!isPidAlive(pid)) {
|
|
93
|
+
console.error(`[omnicode] ERROR: omniroute exited during startup. Log: ${logFile}`);
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (isProcessRunning("omniroute")) {
|
|
98
|
+
console.log(`[omnicode] omniroute started (pid: ${pid})`);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
console.error(`[omnicode] ERROR: omniroute did not become ready. Log: ${logFile}`);
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function stopOmnirouteIfIdle(pidFile) {
|
|
108
|
+
if (isProcessRunning("opencode")) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (existsSync(pidFile)) {
|
|
113
|
+
let pid = null;
|
|
114
|
+
try {
|
|
115
|
+
pid = parseInt(readFileSync(pidFile, "utf8").trim(), 10);
|
|
116
|
+
} catch {}
|
|
117
|
+
if (pid && isPidAlive(pid)) {
|
|
118
|
+
console.log(`[omnicode] no opencode left -> stopping omniroute (pid: ${pid})`);
|
|
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;
|
|
126
|
+
try { process.kill(pid, 0); } catch { break; }
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
try { unlinkSync(pidFile); } catch {}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export async function runRuntime(mode) {
|
|
134
|
+
const dataDir = getDataDir();
|
|
135
|
+
mkdirSync(dataDir, { recursive: true, mode: 0o700 });
|
|
136
|
+
|
|
137
|
+
const logFile = join(dataDir, "omniroute.log");
|
|
138
|
+
const pidFile = join(dataDir, "omniroute.pid");
|
|
139
|
+
|
|
140
|
+
if (!isWindows) {
|
|
141
|
+
process.umask(0o077);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const cleanup = () => stopOmnirouteIfIdle(pidFile);
|
|
145
|
+
process.on("exit", cleanup);
|
|
146
|
+
process.on("SIGINT", () => { cleanup(); process.exit(0); });
|
|
147
|
+
process.on("SIGTERM", () => { cleanup(); process.exit(0); });
|
|
148
|
+
|
|
149
|
+
const pid = startOmniroute(dataDir, logFile, pidFile);
|
|
150
|
+
|
|
151
|
+
await Promise.all([
|
|
152
|
+
pid !== null ? waitForOmniroute(pid, logFile) : Promise.resolve(),
|
|
153
|
+
initTools(dataDir),
|
|
154
|
+
]);
|
|
155
|
+
|
|
156
|
+
if (mode.flag === "-s" && mode.id) {
|
|
157
|
+
console.log(`[omnicode] launching opencode (session: ${mode.id})`);
|
|
158
|
+
await new Promise((resolve) => {
|
|
159
|
+
const child = spawn("opencode", ["-s", mode.id], {
|
|
160
|
+
stdio: "inherit",
|
|
161
|
+
cwd: process.cwd(),
|
|
162
|
+
});
|
|
163
|
+
child.on("close", () => resolve());
|
|
164
|
+
});
|
|
165
|
+
} else {
|
|
166
|
+
console.log("[omnicode] launching opencode (new session)");
|
|
167
|
+
await new Promise((resolve) => {
|
|
168
|
+
const child = spawn("opencode", [], {
|
|
169
|
+
stdio: "inherit",
|
|
170
|
+
cwd: process.cwd(),
|
|
171
|
+
});
|
|
172
|
+
child.on("close", () => resolve());
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
package/src/bin/omnicode.js
CHANGED
|
@@ -1,34 +1,34 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import { existsSync, readFileSync, realpathSync } from "node:fs";
|
|
2
|
+
import { readFileSync, realpathSync } from "node:fs";
|
|
4
3
|
import { join } from "node:path";
|
|
5
4
|
import { fileURLToPath } from "node:url";
|
|
6
|
-
import os from "node:os";
|
|
7
5
|
|
|
8
|
-
import { commandExists } from "../installer/lib.js";
|
|
6
|
+
import { commandExists, getOpencodeDbPath, isProcessRunning } from "../installer/lib.js";
|
|
7
|
+
import { runRuntime } from "./omnicode-runtime.js";
|
|
9
8
|
|
|
10
9
|
const __dirname = fileURLToPath(new URL(".", import.meta.url));
|
|
11
|
-
const runtimeScript = join(__dirname, "omnicode-runtime.sh");
|
|
12
10
|
const packageJsonPath = join(__dirname, "..", "..", "package.json");
|
|
13
11
|
|
|
14
|
-
const SESSION_ID_RE = /^[a-zA-Z0-9_-]+$/;
|
|
12
|
+
const SESSION_ID_RE = /^(?=.{1,128}$)[a-zA-Z0-9_-]+$/;
|
|
13
|
+
|
|
14
|
+
let DatabaseSync;
|
|
15
|
+
try {
|
|
16
|
+
({ DatabaseSync } = await import("node:sqlite"));
|
|
17
|
+
} catch {
|
|
18
|
+
DatabaseSync = null;
|
|
19
|
+
}
|
|
15
20
|
|
|
16
21
|
export function printUsage() {
|
|
17
22
|
console.log(`Usage: omnicode [-s <session_id>] [-c] [--status] [--version]`);
|
|
18
23
|
}
|
|
19
24
|
|
|
25
|
+
let _cachedVersion = null;
|
|
26
|
+
|
|
20
27
|
export function getVersion() {
|
|
28
|
+
if (_cachedVersion !== null) return _cachedVersion;
|
|
21
29
|
const pkg = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
export function isProcessRunning(name) {
|
|
26
|
-
try {
|
|
27
|
-
execFileSync("pgrep", ["-x", name], { stdio: "ignore" });
|
|
28
|
-
return true;
|
|
29
|
-
} catch {
|
|
30
|
-
return false;
|
|
31
|
-
}
|
|
30
|
+
_cachedVersion = pkg.version;
|
|
31
|
+
return _cachedVersion;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
export function getProcessStatus() {
|
|
@@ -78,7 +78,7 @@ export function parseArgs(argv) {
|
|
|
78
78
|
process.exit(2);
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
if (sessionId
|
|
81
|
+
if (!sessionId || !SESSION_ID_RE.test(sessionId)) {
|
|
82
82
|
console.error(`[omnicode] ERROR: invalid session ID format`);
|
|
83
83
|
process.exit(2);
|
|
84
84
|
}
|
|
@@ -87,11 +87,11 @@ export function parseArgs(argv) {
|
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
export async function getLatestSessionId(directory = realpathSync(process.cwd())) {
|
|
90
|
-
const dbPath =
|
|
91
|
-
if (!
|
|
90
|
+
const dbPath = getOpencodeDbPath();
|
|
91
|
+
if (!dbPath) return null;
|
|
92
92
|
|
|
93
93
|
try {
|
|
94
|
-
|
|
94
|
+
if (!DatabaseSync) return null;
|
|
95
95
|
const db = new DatabaseSync(dbPath, { readOnly: true });
|
|
96
96
|
const row = db.prepare(
|
|
97
97
|
"SELECT id FROM session WHERE directory = ? ORDER BY time_updated DESC LIMIT 1"
|
|
@@ -103,21 +103,13 @@ export async function getLatestSessionId(directory = realpathSync(process.cwd())
|
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
export async function resolveSessionMode(sessionId,
|
|
106
|
+
export async function resolveSessionMode(sessionId, latestSessionId = null) {
|
|
107
107
|
if (sessionId) return { flag: "-s", id: sessionId };
|
|
108
108
|
if (latestSessionId === null) latestSessionId = await getLatestSessionId();
|
|
109
109
|
if (latestSessionId) return { flag: "-s", id: latestSessionId };
|
|
110
110
|
return { flag: null, id: null };
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
export function buildRuntimeArgs(mode) {
|
|
114
|
-
const args = [runtimeScript];
|
|
115
|
-
if (mode.flag === "-s") {
|
|
116
|
-
args.push("-s", mode.id);
|
|
117
|
-
}
|
|
118
|
-
return args;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
113
|
async function main() {
|
|
122
114
|
const args = parseArgs(process.argv);
|
|
123
115
|
|
|
@@ -128,23 +120,8 @@ async function main() {
|
|
|
128
120
|
process.exit(1);
|
|
129
121
|
}
|
|
130
122
|
|
|
131
|
-
const mode = await resolveSessionMode(args.sessionId
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
return new Promise((resolve, reject) => {
|
|
135
|
-
const child = spawn("bash", childArgs, {
|
|
136
|
-
stdio: "inherit",
|
|
137
|
-
cwd: process.cwd(),
|
|
138
|
-
});
|
|
139
|
-
child.on("error", reject);
|
|
140
|
-
child.on("close", (code) => {
|
|
141
|
-
if (code !== 0) {
|
|
142
|
-
process.exit(code);
|
|
143
|
-
} else {
|
|
144
|
-
resolve();
|
|
145
|
-
}
|
|
146
|
-
});
|
|
147
|
-
});
|
|
123
|
+
const mode = await resolveSessionMode(args.sessionId);
|
|
124
|
+
await runRuntime(mode);
|
|
148
125
|
}
|
|
149
126
|
|
|
150
127
|
function isMainModule() {
|
package/src/installer/lib.js
CHANGED
|
@@ -1,40 +1,55 @@
|
|
|
1
|
-
import { execFileSync
|
|
2
|
-
import { existsSync
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import os from "node:os";
|
|
5
5
|
|
|
6
|
+
const isWindows = process.platform === "win32";
|
|
7
|
+
|
|
6
8
|
export function commandExists(command) {
|
|
9
|
+
const tool = isWindows ? "where" : "which";
|
|
7
10
|
try {
|
|
8
|
-
execFileSync(
|
|
11
|
+
execFileSync(tool, [command], { stdio: "ignore" });
|
|
9
12
|
return true;
|
|
10
13
|
} catch {
|
|
11
14
|
return false;
|
|
12
15
|
}
|
|
13
16
|
}
|
|
14
17
|
|
|
15
|
-
export function
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
child.on("error", reject);
|
|
26
|
-
child.on("close", (code) => {
|
|
27
|
-
if (code !== 0) {
|
|
28
|
-
reject(new Error(`Command failed with exit code ${code}: ${quoted}`));
|
|
29
|
-
} else {
|
|
30
|
-
resolve();
|
|
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;
|
|
31
28
|
}
|
|
32
|
-
|
|
33
|
-
|
|
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
|
+
|
|
47
|
+
export function getDataDir() {
|
|
48
|
+
return join(os.homedir(), ".local", "share", "omnicode");
|
|
34
49
|
}
|
|
35
50
|
|
|
36
|
-
export function
|
|
37
|
-
const
|
|
38
|
-
if (!existsSync(
|
|
39
|
-
return
|
|
51
|
+
export function getOpencodeDbPath() {
|
|
52
|
+
const dbPath = join(os.homedir(), ".local", "share", "opencode", "opencode.db");
|
|
53
|
+
if (!existsSync(dbPath)) return null;
|
|
54
|
+
return dbPath;
|
|
40
55
|
}
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
set -euo pipefail
|
|
3
|
-
|
|
4
|
-
SESSION_FLAG="${1:-}"
|
|
5
|
-
SESSION_ID="${2:-}"
|
|
6
|
-
RUNTIME_DIR="$HOME/.local/share/omnicode"
|
|
7
|
-
LOG_FILE="$RUNTIME_DIR/omniroute.log"
|
|
8
|
-
GRAYMATTER_LOG="$RUNTIME_DIR/graymatter-init.log"
|
|
9
|
-
OPENSPEC_LOG="$RUNTIME_DIR/openspec-init.log"
|
|
10
|
-
PID_FILE="$RUNTIME_DIR/omniroute.pid"
|
|
11
|
-
MAX_OMNI_WAIT=30
|
|
12
|
-
OMNI_CHECK_DELAY=1
|
|
13
|
-
|
|
14
|
-
export PATH="$PATH:$HOME/.local/bin:/usr/local/bin:/usr/bin:/bin"
|
|
15
|
-
|
|
16
|
-
umask 0077
|
|
17
|
-
mkdir -p "$RUNTIME_DIR"
|
|
18
|
-
|
|
19
|
-
is_pid_alive() {
|
|
20
|
-
local pid="${1:-}"
|
|
21
|
-
[[ -n "$pid" ]] && kill -0 "$pid" >/dev/null 2>&1
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
is_omniroute_running() {
|
|
25
|
-
pgrep -f "omniroute" >/dev/null 2>&1
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
is_opencode_running() {
|
|
29
|
-
pgrep -f "opencode" >/dev/null 2>&1
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
start_omniroute() {
|
|
33
|
-
if is_omniroute_running; then
|
|
34
|
-
echo "[omnicode] omniroute already running"
|
|
35
|
-
return 0
|
|
36
|
-
fi
|
|
37
|
-
|
|
38
|
-
echo "[omnicode] starting omniroute..."
|
|
39
|
-
: > "$LOG_FILE"
|
|
40
|
-
nohup omniroute --no-open >>"$LOG_FILE" 2>&1 &
|
|
41
|
-
local pid=$!
|
|
42
|
-
if [[ -e "$PID_FILE" ]]; then
|
|
43
|
-
echo "[omnicode] ERROR: PID file already exists at $PID_FILE" >&2
|
|
44
|
-
exit 1
|
|
45
|
-
fi
|
|
46
|
-
touch "$PID_FILE"
|
|
47
|
-
chmod 600 "$PID_FILE"
|
|
48
|
-
printf '%s\n' "$pid" > "$PID_FILE"
|
|
49
|
-
|
|
50
|
-
local waited=0
|
|
51
|
-
while [[ "$waited" -lt "$MAX_OMNI_WAIT" ]]; do
|
|
52
|
-
sleep "$OMNI_CHECK_DELAY"
|
|
53
|
-
waited=$((waited + OMNI_CHECK_DELAY))
|
|
54
|
-
|
|
55
|
-
if ! is_pid_alive "$pid"; then
|
|
56
|
-
echo "[omnicode] ERROR: omniroute exited during startup. Log: $LOG_FILE" >&2
|
|
57
|
-
exit 1
|
|
58
|
-
fi
|
|
59
|
-
|
|
60
|
-
if is_omniroute_running; then
|
|
61
|
-
echo "[omnicode] omniroute started (pid: $pid)"
|
|
62
|
-
return 0
|
|
63
|
-
fi
|
|
64
|
-
done
|
|
65
|
-
|
|
66
|
-
echo "[omnicode] ERROR: omniroute did not become ready. Log: $LOG_FILE" >&2
|
|
67
|
-
exit 1
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
stop_omniroute_if_idle() {
|
|
71
|
-
if is_opencode_running; then
|
|
72
|
-
return 0
|
|
73
|
-
fi
|
|
74
|
-
|
|
75
|
-
if [[ -f "$PID_FILE" ]]; then
|
|
76
|
-
local pid
|
|
77
|
-
pid="$(cat "$PID_FILE" 2>/dev/null || true)"
|
|
78
|
-
if is_pid_alive "$pid"; then
|
|
79
|
-
echo "[omnicode] no opencode left -> stopping omniroute (pid: $pid)"
|
|
80
|
-
kill "$pid" 2>/dev/null || true
|
|
81
|
-
fi
|
|
82
|
-
rm -f "$PID_FILE"
|
|
83
|
-
fi
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
cleanup() {
|
|
87
|
-
stop_omniroute_if_idle
|
|
88
|
-
}
|
|
89
|
-
trap cleanup EXIT INT TERM
|
|
90
|
-
|
|
91
|
-
if command -v graymatter >/dev/null 2>&1; then
|
|
92
|
-
echo "[omnicode] graymatter: initializing"
|
|
93
|
-
if graymatter init --only opencode >"$GRAYMATTER_LOG" 2>&1; then
|
|
94
|
-
echo "[omnicode] graymatter: ready"
|
|
95
|
-
else
|
|
96
|
-
echo "[omnicode] WARNING: graymatter init failed; continuing. Log: $GRAYMATTER_LOG"
|
|
97
|
-
fi
|
|
98
|
-
else
|
|
99
|
-
echo "[omnicode] graymatter: not installed, skipping"
|
|
100
|
-
fi
|
|
101
|
-
|
|
102
|
-
if command -v openspec >/dev/null 2>&1; then
|
|
103
|
-
echo "[omnicode] openspec: initializing"
|
|
104
|
-
if openspec init --force --tools opencode >"$OPENSPEC_LOG" 2>&1; then
|
|
105
|
-
echo "[omnicode] openspec: ready"
|
|
106
|
-
else
|
|
107
|
-
echo "[omnicode] WARNING: openspec init failed; continuing. Log: $OPENSPEC_LOG"
|
|
108
|
-
fi
|
|
109
|
-
else
|
|
110
|
-
echo "[omnicode] openspec: not installed, skipping"
|
|
111
|
-
fi
|
|
112
|
-
|
|
113
|
-
start_omniroute
|
|
114
|
-
|
|
115
|
-
if [[ "$SESSION_FLAG" == "-s" && -n "$SESSION_ID" ]]; then
|
|
116
|
-
echo "[omnicode] launching opencode (session: $SESSION_ID)"
|
|
117
|
-
opencode -s "$SESSION_ID"
|
|
118
|
-
else
|
|
119
|
-
echo "[omnicode] launching opencode (new session)"
|
|
120
|
-
opencode
|
|
121
|
-
fi
|