omnius 1.0.530 → 1.0.532

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.
@@ -38,6 +38,7 @@ var SERVICE_LABEL = "omnius-daemon";
38
38
  var LAUNCHD_LABEL = "ai.omnius.daemon";
39
39
  var WIN_TASK_NAME = "OmniusDaemon";
40
40
  var REQUIRED_AIWG_VERSION = "2026.5.11";
41
+ var INSTALL_STEPS = 7;
41
42
 
42
43
  // ─── Helpers ────────────────────────────────────────────────────────────────
43
44
 
@@ -86,6 +87,108 @@ function shellQuote(value) {
86
87
  return "'" + String(value).replace(/'/g, "'\\''") + "'";
87
88
  }
88
89
 
90
+ function userHomeFor(user) {
91
+ if (!user) return HOME;
92
+ try {
93
+ var current = os.userInfo().username;
94
+ if (user === current || user === process.env.USER || user === process.env.LOGNAME) return HOME;
95
+ } catch (e) {}
96
+ if (IS_WIN) return HOME;
97
+ if (IS_MAC) {
98
+ try {
99
+ var macHome = runCapture("dscl . -read /Users/" + shellQuote(user) + " NFSHomeDirectory").split(/\s+/).pop();
100
+ if (macHome) return macHome;
101
+ } catch (e) {}
102
+ }
103
+ try {
104
+ var pwdEntry = runCapture("getent passwd " + shellQuote(user));
105
+ if (pwdEntry) {
106
+ var parts = pwdEntry.split(":");
107
+ if (parts[5]) return parts[5];
108
+ }
109
+ } catch (e) {}
110
+ return path.join("/home", user);
111
+ }
112
+
113
+ function chownToUser(file, user) {
114
+ if (IS_WIN || !user || !process.getuid || process.getuid() !== 0) return;
115
+ try {
116
+ var uid = parseInt(runCapture("id -u " + shellQuote(user)), 10);
117
+ var gid = parseInt(runCapture("id -g " + shellQuote(user)), 10);
118
+ if (Number.isFinite(uid) && Number.isFinite(gid)) fs.chownSync(file, uid, gid);
119
+ } catch (e) {}
120
+ }
121
+
122
+ function envAuthConfigured() {
123
+ var names = [
124
+ "OMNIUS_REST_API_KEYS",
125
+ "OMNIUS_API_KEYS",
126
+ "OMNIUS_REST_API_KEY",
127
+ "OMNIUS_API_KEY",
128
+ "OMNIUS_REST_READ_API_KEY",
129
+ "OMNIUS_READ_API_KEY",
130
+ "OMNIUS_REST_RUN_API_KEY",
131
+ "OMNIUS_RUN_API_KEY",
132
+ "OMNIUS_REST_ADMIN_API_KEY",
133
+ "OMNIUS_ADMIN_API_KEY",
134
+ ];
135
+ for (var i = 0; i < names.length; i++) {
136
+ if (process.env[names[i]] && String(process.env[names[i]]).trim()) return true;
137
+ }
138
+ return false;
139
+ }
140
+
141
+ function runtimeKeysExistForUser(user) {
142
+ try {
143
+ var file = process.env.OMNIUS_RUNTIME_KEYS_FILE || path.join(userHomeFor(user), ".omnius", "keys.json");
144
+ if (!fs.existsSync(file)) return false;
145
+ var parsed = JSON.parse(fs.readFileSync(file, "utf8"));
146
+ return Array.isArray(parsed) && parsed.some(function (rec) { return rec && rec.key && !rec.revoked; });
147
+ } catch (e) {
148
+ return false;
149
+ }
150
+ }
151
+
152
+ function ensureBootstrapApiKeyForUser(user) {
153
+ if (process.env.OMNIUS_SKIP_BOOTSTRAP_KEY === "1") {
154
+ log("OMNIUS_SKIP_BOOTSTRAP_KEY=1 - skipping local REST bootstrap key.");
155
+ return;
156
+ }
157
+ try {
158
+ var home = userHomeFor(user);
159
+ var dir = path.join(home, ".omnius");
160
+ var file = path.join(dir, "api.key");
161
+ fs.mkdirSync(dir, { recursive: true });
162
+ chownToUser(dir, user);
163
+ if (fs.existsSync(file) && String(fs.readFileSync(file, "utf8")).trim()) {
164
+ try { fs.chmodSync(file, 0o600); } catch (e) {}
165
+ chownToUser(file, user);
166
+ log("Local REST bootstrap key already exists: " + file);
167
+ return;
168
+ }
169
+ if (envAuthConfigured()) {
170
+ log("REST auth env key detected - local bootstrap key not created.");
171
+ return;
172
+ }
173
+ if (runtimeKeysExistForUser(user)) {
174
+ log("Runtime REST keys already exist - local bootstrap key not created.");
175
+ return;
176
+ }
177
+ var key = "omnius_bootstrap_" + require("crypto").randomBytes(32).toString("hex");
178
+ var tmp = file + ".tmp." + process.pid + "." + Date.now();
179
+ fs.writeFileSync(tmp, key + "\n", "utf8");
180
+ try { fs.chmodSync(tmp, 0o600); } catch (e) {}
181
+ chownToUser(tmp, user);
182
+ fs.renameSync(tmp, file);
183
+ try { fs.chmodSync(file, 0o600); } catch (e) {}
184
+ chownToUser(file, user);
185
+ log("Created local REST bootstrap key: " + file);
186
+ log("Use /apikey show inside Omnius, then send REST calls with Authorization: Bearer <key>.");
187
+ } catch (e) {
188
+ warn("Could not create local REST bootstrap key (non-fatal): " + (e && e.message ? e.message : String(e)));
189
+ }
190
+ }
191
+
89
192
  function installedGlobalAiwgVersion() {
90
193
  try {
91
194
  var globalRoot = runCapture("npm root -g");
@@ -732,19 +835,22 @@ function repairBrokenWrappers() {
732
835
 
733
836
  function main() {
734
837
  // Always do the nexus cleanup first, regardless of opt-out.
735
- progress(1, 6, "Cleaning stale Nexus runtime state");
838
+ progress(1, INSTALL_STEPS, "Cleaning stale Nexus runtime state");
736
839
  cleanNexus();
737
840
 
738
841
  // Auto-repair stale Omnius model wrappers BEFORE restarting the daemon, so the
739
842
  // freshly-restarted daemon picks up the rebuilt models on first inference.
740
- progress(2, 6, "Checking local model wrappers");
843
+ progress(2, INSTALL_STEPS, "Checking local model wrappers");
741
844
  try { repairBrokenWrappers(); } catch (e) {
742
845
  warn("wrapper auto-repair crashed (non-fatal): " + (e && e.message));
743
846
  }
744
847
 
745
- progress(3, 6, "Ensuring AIWG CLI");
848
+ progress(3, INSTALL_STEPS, "Ensuring AIWG CLI");
746
849
  ensureGlobalAiwgInstall();
747
850
 
851
+ progress(4, INSTALL_STEPS, "Ensuring local REST key");
852
+ ensureBootstrapApiKeyForUser(effectiveUser());
853
+
748
854
  if (process.env.OMNIUS_SKIP_DAEMON_INSTALL === "1") {
749
855
  log("OMNIUS_SKIP_DAEMON_INSTALL=1 — skipping daemon service install.");
750
856
  return safeExit(0);
@@ -756,7 +862,7 @@ function main() {
756
862
  // restart can't reach it, so we have to clean up explicitly. Without this,
757
863
  // the new service-managed daemon fails to bind port 11435 and the user
758
864
  // ends up running stale in-memory code from the previous version.
759
- progress(4, 6, "Clearing daemon port");
865
+ progress(5, INSTALL_STEPS, "Clearing daemon port");
760
866
  forceKillPortHolder(PORT, function (killedCount) {
761
867
  if (killedCount > 0) {
762
868
  log("Killed " + killedCount + " stale daemon process(es) holding port " + PORT + ".");
@@ -787,7 +893,7 @@ function runMainAfterKill() {
787
893
  // idempotent and ensures the unit file matches the current bundle.
788
894
  }
789
895
 
790
- progress(5, 6, "Installing daemon service");
896
+ progress(6, INSTALL_STEPS, "Installing daemon service");
791
897
  log("Installing Omnius API daemon service for port " + PORT + " ...");
792
898
  log(" node: " + nodeBin);
793
899
  log(" omnius script: " + omniusScript);
@@ -818,7 +924,7 @@ function runMainAfterKill() {
818
924
  }
819
925
 
820
926
  // Wait up to 15s for /health to come up, but don't fail npm install.
821
- progress(6, 6, "Verifying daemon health");
927
+ progress(7, INSTALL_STEPS, "Verifying daemon health");
822
928
  waitForHealth(15000, function (healthy) {
823
929
  if (healthy) {
824
930
  log("Omnius API daemon is live: http://127.0.0.1:" + PORT + "/health");
@@ -28,6 +28,11 @@ Auth header:
28
28
  Authorization: Bearer <key>
29
29
  ```
30
30
 
31
+ Local global installs create a daemon bootstrap key at `~/.omnius/api.key`.
32
+ Inside Omnius, run `/apikey show` to display it, `/apikey copy` to copy it, and
33
+ `/apikey mint run` to create a project runtime key for agents or MCP-style
34
+ REST clients.
35
+
31
36
  ## Health
32
37
 
33
38
  ```bash
@@ -131,5 +136,5 @@ Requires admin scope:
131
136
  curl -s -X POST http://127.0.0.1:11435/v1/keys \
132
137
  -H 'authorization: Bearer admin-secret' \
133
138
  -H 'content-type: application/json' \
134
- -d '{"scope":"run","owner":"ci","rpm":60,"tpd":100000,"max_jobs":3}'
139
+ -d '{"scope":"run","project":"myactuator","rpm":60,"tpd":100000,"max_jobs":3}'
135
140
  ```
@@ -37,7 +37,9 @@ Fields:
37
37
 
38
38
  ## Runtime Keys
39
39
 
40
- Runtime keys are stored under `~/.omnius/keys.json` and are checked after environment keys.
40
+ Runtime keys are stored under `~/.omnius/keys.json` by default and are checked after environment keys and the local bootstrap key. Override the store with `OMNIUS_RUNTIME_KEYS_FILE` or put all Omnius state under another directory with `OMNIUS_CONFIG_HOME`.
41
+
42
+ Global installs create a local daemon bootstrap key at `~/.omnius/api.key` if one does not exist. The daemon accepts it as an admin token, so a local operator can start Omnius and run `/apikey show` to reveal it, or `/apikey mint run` to create a project runtime key.
41
43
 
42
44
  | Method | Path | Scope | Purpose |
43
45
  | --- | --- | --- | --- |
@@ -50,7 +52,8 @@ Mint body:
50
52
  ```json
51
53
  {
52
54
  "scope": "run",
53
- "owner": "ci",
55
+ "project": "myactuator",
56
+ "owner": "project:myactuator",
54
57
  "profile": "readonly",
55
58
  "rpm": 60,
56
59
  "tpd": 100000,
@@ -58,6 +61,16 @@ Mint body:
58
61
  }
59
62
  ```
60
63
 
64
+ If `owner` is omitted and `project` is provided, Omnius uses `project:<project>` as the audit owner.
65
+
66
+ Agent and MCP-style clients should persist the returned secret in their own secret store and send it as:
67
+
68
+ ```text
69
+ Authorization: Bearer <key>
70
+ ```
71
+
72
+ Do not put API keys in query strings. Omnius rejects query-string keys to keep them out of logs and browser history.
73
+
61
74
  ## Scope Semantics
62
75
 
63
76
  `read` is for inspection:
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.530",
3
+ "version": "1.0.532",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "omnius",
9
- "version": "1.0.530",
9
+ "version": "1.0.532",
10
10
  "bundleDependencies": [
11
11
  "image-to-ascii"
12
12
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.530",
3
+ "version": "1.0.532",
4
4
  "description": "AI coding agent powered by open-source models (Ollama/vLLM) — interactive TUI with agentic tool-calling loop",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",