omnirush 0.8.5 → 0.9.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/assets/CHANGELOG.md +85 -0
- package/assets/collect-once.ts +6 -5
- package/assets/extensions/omnirush/agents-lib.ts +196 -12
- package/assets/extensions/omnirush/agents.ts +97 -105
- package/assets/extensions/omnirush/auth.js +399 -115
- package/assets/extensions/omnirush/bgshell-lib.ts +400 -0
- package/assets/extensions/omnirush/bgshell.ts +392 -0
- package/assets/extensions/omnirush/collector.ts +103 -16
- package/assets/extensions/omnirush/commands.ts +14 -15
- package/assets/extensions/omnirush/deliveries.ts +145 -0
- package/assets/extensions/omnirush/guard/UPSTREAM +2 -0
- package/assets/extensions/omnirush/guard/git-command-policy.ts +885 -0
- package/assets/extensions/omnirush/guard-lib.ts +230 -0
- package/assets/extensions/omnirush/guard.ts +340 -0
- package/assets/extensions/omnirush/index.ts +12 -0
- package/assets/extensions/omnirush/pi-engine.ts +65 -1
- package/assets/extensions/omnirush/sota.ts +104 -47
- package/assets/extensions/omnirush/status-lib.ts +3 -0
- package/assets/extensions/omnirush/subagents-lib.ts +623 -0
- package/assets/extensions/omnirush/subagents.ts +305 -0
- package/assets/extensions/omnirush/swarm-lib.ts +142 -0
- package/assets/extensions/omnirush/swarm.ts +95 -0
- package/assets/extensions/omnirush/voice/capture.ts +502 -0
- package/assets/extensions/omnirush/voice/core/UPSTREAM +16 -0
- package/assets/extensions/omnirush/voice/core/file-source.ts +70 -0
- package/assets/extensions/omnirush/voice/core/index.ts +21 -0
- package/assets/extensions/omnirush/voice/core/keyterms.ts +117 -0
- package/assets/extensions/omnirush/voice/core/resample.ts +63 -0
- package/assets/extensions/omnirush/voice/core/segmenter.ts +231 -0
- package/assets/extensions/omnirush/voice/core/session.ts +403 -0
- package/assets/extensions/omnirush/voice/core/text.ts +81 -0
- package/assets/extensions/omnirush/voice/core/transcriber.ts +135 -0
- package/assets/extensions/omnirush/voice/core/types.ts +102 -0
- package/assets/extensions/omnirush/voice/core/wav.ts +95 -0
- package/assets/extensions/omnirush/voice/keys.ts +435 -0
- package/assets/extensions/omnirush/voice/kitty.ts +64 -0
- package/assets/extensions/omnirush/voice/pvrecorder-worker.cjs +43 -0
- package/assets/extensions/omnirush/voice/settings.ts +67 -0
- package/assets/extensions/omnirush/voice.ts +838 -0
- package/assets/extensions/omnirush/yolo-lib.ts +80 -0
- package/assets/extensions/omnirush/yolo.ts +85 -0
- package/package.json +6 -2
- package/scripts/brand-engine.js +526 -0
- package/scripts/build-all-packages.py +29 -1
- package/scripts/smoke-packages.py +32 -1
- package/src/bin.js +252 -53
- package/src/compat.js +272 -0
- package/src/lib.js +64 -0
- package/scripts/patch-pi-branding.js +0 -251
package/src/bin.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// omnirush — launcher for the Omnirush coding agent
|
|
2
|
+
// omnirush — launcher for the Omnirush coding agent.
|
|
3
3
|
//
|
|
4
4
|
// The CLI is a thin client of the Omnirush manager (a single public
|
|
5
5
|
// HTTPS origin, real cert — no helper proxy, no custom CA):
|
|
@@ -8,15 +8,18 @@
|
|
|
8
8
|
// stored 0600 in ~/.omnirush/auth.json)
|
|
9
9
|
// omnirush logout forget the stored tokens
|
|
10
10
|
// omnirush whoami show the signed-in device identity
|
|
11
|
-
// omnirush doctor check auth (or pending login), manager,
|
|
11
|
+
// omnirush doctor check auth (or pending login), manager, agent config
|
|
12
12
|
// omnirush collect one-shot workspace snapshot upload
|
|
13
|
-
// omnirush
|
|
13
|
+
// omnirush update update omnirush from npm
|
|
14
|
+
// omnirush run the agent (default; forwards to the agent core)
|
|
14
15
|
//
|
|
15
16
|
// Environment:
|
|
16
17
|
// OMNIRUSH_ORIGIN manager origin override (default
|
|
17
18
|
// https://omnirush.ai/omnirush)
|
|
18
19
|
// OMNIRUSH_GATEWAY_URL provider base URL override ({origin}/v1)
|
|
19
20
|
// OMNIRUSH_DIR state dir override (default ~/.omnirush)
|
|
21
|
+
// OMNIRUSH_AGENT_DIR agent data dir override (default <state dir>/agent;
|
|
22
|
+
// OMNIRUSH_PI_AGENT_DIR is the older spelling)
|
|
20
23
|
// OMNIRUSH_MODEL model override for plain runs (default
|
|
21
24
|
// gpt-6-astra; gpt-6-sol, gpt-5.6-sol and the Muse
|
|
22
25
|
// models also available; user
|
|
@@ -47,8 +50,11 @@ import {
|
|
|
47
50
|
withModelDefaults,
|
|
48
51
|
withDefaultThinkingLevel,
|
|
49
52
|
resolveModelDefaults,
|
|
53
|
+
resolveYolo,
|
|
54
|
+
yoloBanner,
|
|
50
55
|
} from "./lib.js";
|
|
51
56
|
import { installManagedTools } from "./tools.js";
|
|
57
|
+
import { engineEnvAliases, migrateAgentDir, projectConfigDirName, resolveAgentDir } from "./compat.js";
|
|
52
58
|
import { ensureRuntime, managedBunPath } from "../scripts/postinstall.js";
|
|
53
59
|
import {
|
|
54
60
|
clearAuth,
|
|
@@ -60,23 +66,53 @@ import {
|
|
|
60
66
|
openBrowser,
|
|
61
67
|
pollForToken,
|
|
62
68
|
redactForDisplay,
|
|
63
|
-
refreshTokens,
|
|
64
69
|
saveAuth,
|
|
65
70
|
savePendingFlow,
|
|
66
71
|
clearPendingFlow,
|
|
72
|
+
createRefresher,
|
|
73
|
+
sessionEndedMessage,
|
|
67
74
|
} from "../assets/extensions/omnirush/auth.js";
|
|
68
75
|
|
|
69
76
|
const require = createRequire(import.meta.url);
|
|
70
77
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
71
78
|
const OMNI_DIR = omniDir();
|
|
72
|
-
|
|
73
|
-
//
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
79
|
+
process.title = "omnirush";
|
|
80
|
+
// The agent core's data (settings, models.json, provider auth, sessions,
|
|
81
|
+
// extensions): <state dir>/agent, or OMNIRUSH_AGENT_DIR. Releases up to
|
|
82
|
+
// 0.8.x used the core's upstream default folder; ensureDirs() copies it
|
|
83
|
+
// over once (src/compat.js) and falls back to it for a run whose copy failed.
|
|
84
|
+
const AGENT = resolveAgentDir(process.env, { stateDir: OMNI_DIR });
|
|
85
|
+
let PI_AGENT_DIR, PI_MODELS_JSON, PI_SETTINGS_JSON, PI_EXT_DIR;
|
|
86
|
+
function useAgentDir(dir) {
|
|
87
|
+
PI_AGENT_DIR = dir;
|
|
88
|
+
PI_MODELS_JSON = path.join(dir, "models.json");
|
|
89
|
+
PI_SETTINGS_JSON = path.join(dir, "settings.json");
|
|
90
|
+
PI_EXT_DIR = path.join(dir, "extensions", "omnirush");
|
|
91
|
+
}
|
|
92
|
+
useAgentDir(AGENT.dir);
|
|
93
|
+
|
|
94
|
+
/** One-time copy of the pre-0.9 agent data into the new folder (see src/compat.js). */
|
|
95
|
+
function migrateLegacyAgentData() {
|
|
96
|
+
if (!AGENT.legacyDir) return;
|
|
97
|
+
const result = migrateAgentDir({
|
|
98
|
+
from: AGENT.legacyDir,
|
|
99
|
+
to: AGENT.dir,
|
|
100
|
+
onStart: () => console.error(`omnirush: copying your sessions and agent settings to ${AGENT.dir} (one time)...`),
|
|
101
|
+
});
|
|
102
|
+
if (result.status === "migrated") {
|
|
103
|
+
console.error(
|
|
104
|
+
`omnirush: done (${result.copied} files copied${result.merged ? `, ${result.merged} merged` : ""}; ` +
|
|
105
|
+
"the previous folder was left as it was).",
|
|
106
|
+
);
|
|
107
|
+
} else if (result.status === "failed") {
|
|
108
|
+
// Never lose data: run on the previous folder and retry next launch.
|
|
109
|
+
console.error(
|
|
110
|
+
`omnirush: could not copy your agent data to ${AGENT.dir} (${result.error?.message ?? result.error}); ` +
|
|
111
|
+
"using the previous folder for this run, retrying next time.",
|
|
112
|
+
);
|
|
113
|
+
useAgentDir(AGENT.legacyDir);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
80
116
|
|
|
81
117
|
function managerOrigin() {
|
|
82
118
|
return (
|
|
@@ -110,11 +146,12 @@ function piPackage() {
|
|
|
110
146
|
function piEntry() {
|
|
111
147
|
const found = piPackage();
|
|
112
148
|
if (found?.pkg?.bin?.pi) return path.join(found.dir, found.pkg.bin.pi);
|
|
113
|
-
throw new Error("could not locate the pinned
|
|
149
|
+
throw new Error("could not locate the pinned agent core (broken install). Fix: npm install -g omnirush@latest");
|
|
114
150
|
}
|
|
115
151
|
|
|
116
152
|
function ensureDirs() {
|
|
117
153
|
fs.mkdirSync(OMNI_DIR, { recursive: true, mode: 0o700 });
|
|
154
|
+
migrateLegacyAgentData();
|
|
118
155
|
fs.mkdirSync(PI_AGENT_DIR, { recursive: true });
|
|
119
156
|
fs.mkdirSync(PI_EXT_DIR, { recursive: true });
|
|
120
157
|
try {
|
|
@@ -162,10 +199,21 @@ async function launchModels(gatewayUrl, accessToken) {
|
|
|
162
199
|
const base = String(gatewayUrl).replace(/\/+$/, "");
|
|
163
200
|
if (process.env.OMNIRUSH_STATIC_MODELS !== "1" && accessToken) {
|
|
164
201
|
try {
|
|
165
|
-
const
|
|
166
|
-
headers: { Authorization: `Bearer ${
|
|
202
|
+
const get = (token) => fetch(`${base}/models`, {
|
|
203
|
+
headers: { Authorization: `Bearer ${token}`, Accept: "application/json" },
|
|
167
204
|
signal: AbortSignal.timeout(2500),
|
|
168
205
|
});
|
|
206
|
+
let response = await get(accessToken);
|
|
207
|
+
if (response.status === 401) {
|
|
208
|
+
// The stored access token expired (or another process rotated):
|
|
209
|
+
// refresh now, under the shared lock, so the agent starts with a
|
|
210
|
+
// working pair on disk.
|
|
211
|
+
await response.body?.cancel().catch(() => undefined);
|
|
212
|
+
const outcome = await createRefresher({ dir: OMNI_DIR, origin: managerOrigin() })
|
|
213
|
+
.recover(accessToken)
|
|
214
|
+
.catch(() => null);
|
|
215
|
+
if (outcome?.status === "ok") response = await get(outcome.accessToken);
|
|
216
|
+
}
|
|
169
217
|
if (response.ok) {
|
|
170
218
|
const payload = await response.json();
|
|
171
219
|
const models = catalogFromGateway(payload);
|
|
@@ -221,7 +269,7 @@ function installExtensions() {
|
|
|
221
269
|
fs.mkdirSync(to, { recursive: true });
|
|
222
270
|
for (const entry of fs.readdirSync(from, { withFileTypes: true })) {
|
|
223
271
|
if (entry.isDirectory()) copy(path.join(from, entry.name), path.join(to, entry.name));
|
|
224
|
-
else if (entry.name.endsWith(".ts") || entry.name.endsWith(".js")) {
|
|
272
|
+
else if (entry.name.endsWith(".ts") || entry.name.endsWith(".js") || entry.name.endsWith(".cjs")) {
|
|
225
273
|
fs.copyFileSync(path.join(from, entry.name), path.join(to, entry.name));
|
|
226
274
|
}
|
|
227
275
|
}
|
|
@@ -444,15 +492,24 @@ async function cmdLogout() {
|
|
|
444
492
|
console.log(`Signed out. Credentials removed from ${OMNI_DIR}`);
|
|
445
493
|
}
|
|
446
494
|
|
|
447
|
-
|
|
495
|
+
/**
|
|
496
|
+
* GET /device/me with the current on-disk token; a 401 adopts a pair
|
|
497
|
+
* another process already rotated, or refreshes under the shared refresh
|
|
498
|
+
* lock (never a bare refresh that could race a running agent). Resolves
|
|
499
|
+
* {me} or {me: null, outcome} with the recover() verdict.
|
|
500
|
+
*/
|
|
501
|
+
async function fetchMeWithRefresh() {
|
|
448
502
|
const origin = managerOrigin();
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
503
|
+
const refresher = createRefresher({ dir: OMNI_DIR, origin });
|
|
504
|
+
let token = refresher.accessToken();
|
|
505
|
+
for (let round = 0; round < 3; round++) {
|
|
506
|
+
const me = await deviceMe(origin, { accessToken: token, fetchImpl: globalThis.fetch });
|
|
507
|
+
if (me !== null) return { me };
|
|
508
|
+
const outcome = await refresher.recover(token);
|
|
509
|
+
if (outcome.status !== "ok") return { me: null, outcome };
|
|
510
|
+
token = outcome.accessToken;
|
|
511
|
+
}
|
|
512
|
+
return { me: null, outcome: { status: "dead", reason: "session", detail: "access token refused after refresh" } };
|
|
456
513
|
}
|
|
457
514
|
|
|
458
515
|
async function cmdWhoami() {
|
|
@@ -461,9 +518,13 @@ async function cmdWhoami() {
|
|
|
461
518
|
console.error("Omnirush: not signed in. Run `omnirush login` first.");
|
|
462
519
|
process.exit(1);
|
|
463
520
|
}
|
|
464
|
-
const { me } = await fetchMeWithRefresh(
|
|
521
|
+
const { me, outcome } = await fetchMeWithRefresh();
|
|
465
522
|
if (!me) {
|
|
466
|
-
console.error(
|
|
523
|
+
console.error(
|
|
524
|
+
outcome?.status === "transient"
|
|
525
|
+
? `Omnirush: cannot reach the manager to verify the session (${outcome.error}) — try again shortly.`
|
|
526
|
+
: sessionEndedMessage(outcome),
|
|
527
|
+
);
|
|
467
528
|
process.exit(1);
|
|
468
529
|
}
|
|
469
530
|
console.log(JSON.stringify(redactForDisplay(me), null, 2));
|
|
@@ -491,21 +552,19 @@ async function cmdDoctor() {
|
|
|
491
552
|
const auth = loadAuth(OMNI_DIR);
|
|
492
553
|
if (auth) {
|
|
493
554
|
try {
|
|
494
|
-
const { me } = await fetchMeWithRefresh(
|
|
555
|
+
const { me, outcome } = await fetchMeWithRefresh();
|
|
495
556
|
if (me) {
|
|
496
557
|
const label =
|
|
497
558
|
(me && typeof me === "object" && (me.device_name || me.email || me.user_id || me.id)) ||
|
|
498
559
|
"device recognized";
|
|
499
560
|
ok("auth", true, `signed in (${label})`);
|
|
561
|
+
} else if (outcome?.status === "transient") {
|
|
562
|
+
ok("auth", false, `cannot verify session (${outcome.error})`);
|
|
500
563
|
} else {
|
|
501
|
-
ok("auth", false,
|
|
564
|
+
ok("auth", false, sessionEndedMessage(outcome).replace(/^Omnirush: /, ""));
|
|
502
565
|
}
|
|
503
566
|
} catch (e) {
|
|
504
|
-
|
|
505
|
-
ok("auth", false, "session expired — run `omnirush login` again");
|
|
506
|
-
} else {
|
|
507
|
-
ok("auth", false, `cannot verify session (${e.message})`);
|
|
508
|
-
}
|
|
567
|
+
ok("auth", false, `cannot verify session (${e.message})`);
|
|
509
568
|
}
|
|
510
569
|
} else {
|
|
511
570
|
const pending = loadPendingFlow(OMNI_DIR);
|
|
@@ -706,21 +765,33 @@ function childEnv(auth) {
|
|
|
706
765
|
const origin = (process.env.OMNIRUSH_ORIGIN || "").trim().replace(/\/+$/, "") ||
|
|
707
766
|
gateway.replace(/\/+$/, "").replace(/\/v1$/, ""); // provider base -> manager origin
|
|
708
767
|
return {
|
|
768
|
+
// OMNIRUSH_* spellings of the core's own settings (PI_* still work).
|
|
769
|
+
...engineEnvAliases(process.env),
|
|
770
|
+
// Bootstrap values only: the agent (and every sub-agent it spawns)
|
|
771
|
+
// re-reads the shared auth file in OMNIRUSH_DIR for each request, so
|
|
772
|
+
// these never go stale in a way that matters.
|
|
709
773
|
OMNIRUSH_TOKEN: auth.accessToken,
|
|
710
774
|
OMNIRUSH_ACCESS_TOKEN: auth.accessToken,
|
|
711
775
|
OMNIRUSH_ORIGIN: origin,
|
|
712
776
|
OMNIRUSH_GATEWAY_URL: gateway,
|
|
713
777
|
OMNIRUSH_DIR: OMNI_DIR,
|
|
714
778
|
// /status shows the CLI version; extensions have no package.json of
|
|
715
|
-
// their own in
|
|
779
|
+
// their own in the agent dir, so the launcher hands it over.
|
|
716
780
|
OMNIRUSH_VERSION: ownVersion(),
|
|
717
781
|
// The collector reports it as the environment's engine_version.
|
|
718
782
|
OMNIRUSH_ENGINE_VERSION: engineVersion(),
|
|
783
|
+
// Where this package lives: extensions (copied to the agent dir) load optional
|
|
784
|
+
// native dependencies from here (voice: @picovoice/pvrecorder-node).
|
|
785
|
+
OMNIRUSH_PACKAGE_ROOT: path.resolve(__dirname, ".."),
|
|
719
786
|
// The agent resolves its config dir from <APP>_CODING_AGENT_DIR. The
|
|
720
787
|
// branding patch renames the app to omnirush, so set the omnirush
|
|
721
788
|
// spelling; the pi spelling stays for unpatched cores.
|
|
722
789
|
OMNIRUSH_CODING_AGENT_DIR: PI_AGENT_DIR,
|
|
723
790
|
PI_CODING_AGENT_DIR: PI_AGENT_DIR,
|
|
791
|
+
// Project settings live in <project>/.omnirush; a project that only
|
|
792
|
+
// has the pre-0.9 folder keeps using that one (the branding patch
|
|
793
|
+
// makes the core read this variable).
|
|
794
|
+
OMNIRUSH_PROJECT_CONFIG_DIR: projectConfigDirName(process.cwd()),
|
|
724
795
|
// The agent core is a fork/rebrand: never phone home to pi.dev.
|
|
725
796
|
// PI_OFFLINE kills startup update checks (the "Update Available —
|
|
726
797
|
// pi.dev/changelog" banner), package updates, and install/update
|
|
@@ -778,6 +849,32 @@ function spawnAgent(cmd, args, opts) {
|
|
|
778
849
|
return child;
|
|
779
850
|
}
|
|
780
851
|
|
|
852
|
+
/** The saved /yolo setting ("yolo": true in <state dir>/settings.json). */
|
|
853
|
+
function yoloSetting() {
|
|
854
|
+
try {
|
|
855
|
+
return JSON.parse(fs.readFileSync(path.join(OMNI_DIR, "settings.json"), "utf8"))?.yolo === true;
|
|
856
|
+
} catch {
|
|
857
|
+
return false;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
/**
|
|
862
|
+
* Yolo mode for an agent launch: strips --yolo/--no-yolo, adds the core's
|
|
863
|
+
* --approve (project trusted for the run) and hands the state to the
|
|
864
|
+
* extensions (OMNIRUSH_YOLO). Non-interactive runs get the banner on
|
|
865
|
+
* stderr here; the TUI shows it itself (yolo.ts).
|
|
866
|
+
*/
|
|
867
|
+
function launchYolo(args) {
|
|
868
|
+
const resolved = resolveYolo(args, { env: process.env, setting: yoloSetting() });
|
|
869
|
+
const env = { OMNIRUSH_YOLO: resolved.yolo ? "1" : "0", OMNIRUSH_YOLO_SOURCE: resolved.source };
|
|
870
|
+
if (resolved.yolo) {
|
|
871
|
+
env.OMNIRUSH_YOLO_TRUSTED = resolved.trusted ? "1" : "0";
|
|
872
|
+
const headless = resolved.args.some((a) => a === "-p" || a === "--print" || a === "--mode") || !process.stdout.isTTY;
|
|
873
|
+
if (headless) console.error(`omnirush: ${yoloBanner(resolved)}`);
|
|
874
|
+
}
|
|
875
|
+
return { args: resolved.args, env };
|
|
876
|
+
}
|
|
877
|
+
|
|
781
878
|
async function cmdRun() {
|
|
782
879
|
ensureDirs();
|
|
783
880
|
const auth = requireAuth();
|
|
@@ -787,10 +884,13 @@ async function cmdRun() {
|
|
|
787
884
|
const { cmd, args } = await runtimeFor(entry);
|
|
788
885
|
// Plain `omnirush` always runs the SOTA model (charter rule 1); user
|
|
789
886
|
// flags win when present.
|
|
790
|
-
|
|
887
|
+
// `omnirush run` takes no other arguments; only yolo's --approve passes.
|
|
888
|
+
const yolo = launchYolo(process.argv.slice(3).filter((a) => a === "--yolo" || a === "--no-yolo"));
|
|
889
|
+
const piArgs = withModelDefaults(yolo.args, resolveModelDefaults(process.env));
|
|
791
890
|
spawnAgent(cmd, [...args, ...piArgs], {
|
|
792
891
|
stdio: "inherit",
|
|
793
|
-
|
|
892
|
+
// The pair may have rotated during launch (model catalog refresh).
|
|
893
|
+
env: { ...process.env, ...childEnv(loadAuth(OMNI_DIR) ?? auth), ...yolo.env },
|
|
794
894
|
});
|
|
795
895
|
}
|
|
796
896
|
|
|
@@ -827,8 +927,18 @@ Options:
|
|
|
827
927
|
gpt-5.6-sol, meta-muse-spark, muse-spark-1.1;
|
|
828
928
|
effort: minimal..max)
|
|
829
929
|
--thinking <level> reasoning effort override
|
|
930
|
+
--subagent-model <id|same> model the sub-agents run on for this run
|
|
931
|
+
(one of your account's models; default: the
|
|
932
|
+
one picked with /subagents, else the main model)
|
|
933
|
+
--subagent-effort <level|same> sub-agent effort for this run (limited to
|
|
934
|
+
that model's levels)
|
|
830
935
|
--strict-sota exit non-zero if the served model is not
|
|
831
936
|
the requested SOTA model
|
|
937
|
+
--yolo yolo mode for this run: nothing asks, the
|
|
938
|
+
project folder is trusted, every command is
|
|
939
|
+
allowed (default: guarded mode, where git
|
|
940
|
+
writes, destructive commands and sudo ask
|
|
941
|
+
first; --no-yolo turns a saved /yolo on off)
|
|
832
942
|
--session <file> | --continue session options (forwarded to the agent)
|
|
833
943
|
-p, --print "<prompt>" print-mode: answer once and exit
|
|
834
944
|
-h, --help show this help
|
|
@@ -838,6 +948,15 @@ Session commands (inside the agent):
|
|
|
838
948
|
/usage detailed usage from the manager (counters + devices)
|
|
839
949
|
/status session status (identity, model, grant, collector)
|
|
840
950
|
/mcp MCP server state (/mcp reconnect [server])
|
|
951
|
+
/subagents model and effort of the sub-agents (/subagents model <id|same>,
|
|
952
|
+
/subagents effort <level|same>; saved for the next runs)
|
|
953
|
+
/agents sub-agents of this session (/agents cancel <id|all>)
|
|
954
|
+
/jobs background shell commands (/jobs kill <id|all>)
|
|
955
|
+
/voice dictate prompts: hold Space to talk (/voice hold|tap|off|status)
|
|
956
|
+
/mode approval mode: guarded (asks before git writes and destructive
|
|
957
|
+
commands) or yolo (/mode guarded|yolo; saved); /approvals reset
|
|
958
|
+
forgets this session's "allow for this session" answers
|
|
959
|
+
/yolo yolo mode: no prompts, project trusted (/yolo on|off; saved)
|
|
841
960
|
/verify model attestation (arrives with the gateway lane)
|
|
842
961
|
|
|
843
962
|
Environment:
|
|
@@ -845,11 +964,106 @@ Environment:
|
|
|
845
964
|
OMNIRUSH_MODEL default model override (e.g. gpt-6-sol)
|
|
846
965
|
OMNIRUSH_STATIC_MODELS=1 use the shipped model list (no GET /v1/models)
|
|
847
966
|
OMNIRUSH_DIR state dir override (default ~/.omnirush)
|
|
967
|
+
OMNIRUSH_AGENT_DIR agent data dir override (default ~/.omnirush/agent)
|
|
848
968
|
OMNIRUSH_DEBUG=1 verbose diagnostics (raw gateway errors on failure)
|
|
969
|
+
OMNIRUSH_SUBAGENT_MODEL / OMNIRUSH_SUBAGENT_EFFORT
|
|
970
|
+
sub-agent model / effort for this run (as the flags)
|
|
971
|
+
OMNIRUSH_YOLO=1 yolo mode (as --yolo; OMNIRUSH_YOLO=0 turns it off)
|
|
972
|
+
OMNIRUSH_BASH_FOREGROUND_SECONDS
|
|
973
|
+
how long a shell command runs in the foreground before
|
|
974
|
+
it continues in the background (default 300)
|
|
975
|
+
OMNIRUSH_<SETTING> the agent core's own settings (e.g. OMNIRUSH_CACHE_RETENTION;
|
|
976
|
+
the older PI_<SETTING> names still work)
|
|
849
977
|
|
|
850
978
|
Docs: https://omnirush.ai/omnirush — state is stored 0600 in ~/.omnirush`);
|
|
851
979
|
}
|
|
852
980
|
|
|
981
|
+
/**
|
|
982
|
+
* `omnirush update` updates omnirush itself from npm. The agent core's own
|
|
983
|
+
* self-update would install its stock upstream package instead, so only
|
|
984
|
+
* package and model-catalog updates (--extensions, --extension <source>,
|
|
985
|
+
* --models, a package source) are handed to the core; --all does both.
|
|
986
|
+
*/
|
|
987
|
+
async function cmdUpdate(args) {
|
|
988
|
+
if (args.includes("-h") || args.includes("--help")) return runCore(["update", ...args]);
|
|
989
|
+
const coreArgs = [];
|
|
990
|
+
let self = false;
|
|
991
|
+
let coreTarget = false;
|
|
992
|
+
for (let i = 0; i < args.length; i++) {
|
|
993
|
+
const arg = args[i];
|
|
994
|
+
// "pi" is the core's own alias for self: never let it reach the core.
|
|
995
|
+
if (arg === "--self" || arg === "self" || arg === "omnirush" || arg === "pi") self = true;
|
|
996
|
+
else if (arg === "--all") {
|
|
997
|
+
self = true;
|
|
998
|
+
coreTarget = true;
|
|
999
|
+
coreArgs.push("--extensions");
|
|
1000
|
+
} else if (arg === "--force") continue;
|
|
1001
|
+
else {
|
|
1002
|
+
if (arg === "--extensions" || arg === "--models" || arg === "--extension" || !arg.startsWith("-")) coreTarget = true;
|
|
1003
|
+
coreArgs.push(arg);
|
|
1004
|
+
if (arg === "--extension" && i + 1 < args.length) coreArgs.push(args[++i]);
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
if (!coreTarget) self = true;
|
|
1008
|
+
if (self) {
|
|
1009
|
+
const status = selfUpdate();
|
|
1010
|
+
if (status !== 0) process.exit(status);
|
|
1011
|
+
}
|
|
1012
|
+
if (coreTarget) await runCore(["update", ...coreArgs]);
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
function selfUpdate() {
|
|
1016
|
+
const { spawnSync } = require("node:child_process");
|
|
1017
|
+
const npm = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
1018
|
+
const packageRoot = path.resolve(__dirname, "..");
|
|
1019
|
+
let globalRoot = "";
|
|
1020
|
+
try {
|
|
1021
|
+
globalRoot = String(
|
|
1022
|
+
spawnSync(npm, ["root", "-g"], { encoding: "utf8", shell: process.platform === "win32", timeout: 30_000 }).stdout || "",
|
|
1023
|
+
).trim();
|
|
1024
|
+
} catch {
|
|
1025
|
+
/* npm missing: handled below */
|
|
1026
|
+
}
|
|
1027
|
+
const npmManaged =
|
|
1028
|
+
!!globalRoot && path.relative(path.resolve(globalRoot), packageRoot) === "omnirush";
|
|
1029
|
+
if (!npmManaged) {
|
|
1030
|
+
console.error(
|
|
1031
|
+
`omnirush: this copy (${packageRoot}) was not installed with \`npm install -g omnirush\`. ` +
|
|
1032
|
+
"Update it the way you installed it, or switch to the npm package: npm install -g omnirush@latest",
|
|
1033
|
+
);
|
|
1034
|
+
return 1;
|
|
1035
|
+
}
|
|
1036
|
+
console.log(`Updating omnirush ${ownVersion()}: npm install -g omnirush@latest`);
|
|
1037
|
+
const result = spawnSync(npm, ["install", "-g", "omnirush@latest"], {
|
|
1038
|
+
stdio: "inherit",
|
|
1039
|
+
shell: process.platform === "win32",
|
|
1040
|
+
});
|
|
1041
|
+
if (result.status !== 0) {
|
|
1042
|
+
console.error("omnirush update failed. Run it yourself: npm install -g omnirush@latest");
|
|
1043
|
+
return result.status || 1;
|
|
1044
|
+
}
|
|
1045
|
+
return 0;
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
// Forward everything else (including argv[0] — e.g. `-p prompt`,
|
|
1049
|
+
// `--provider x`, package subcommands) to the agent core, defaulting the
|
|
1050
|
+
// model the same way `omnirush run` does; user-specified --provider/--model
|
|
1051
|
+
// are never duplicated and core subcommands pass through untouched.
|
|
1052
|
+
async function runCore(args) {
|
|
1053
|
+
ensureDirs();
|
|
1054
|
+
const auth = requireAuth();
|
|
1055
|
+
installModelsJson(agentGatewayUrl(auth), await launchModels(agentGatewayUrl(auth), auth.accessToken));
|
|
1056
|
+
installExtensions();
|
|
1057
|
+
const entry = piEntry();
|
|
1058
|
+
const { cmd: bin, args: runtimeArgs } = await runtimeFor(entry);
|
|
1059
|
+
const yolo = launchYolo(args);
|
|
1060
|
+
const piArgs = withModelDefaults(yolo.args, resolveModelDefaults(process.env));
|
|
1061
|
+
spawnAgent(bin, [...runtimeArgs, ...piArgs], {
|
|
1062
|
+
stdio: "inherit",
|
|
1063
|
+
env: { ...process.env, ...childEnv(auth), ...yolo.env },
|
|
1064
|
+
});
|
|
1065
|
+
}
|
|
1066
|
+
|
|
853
1067
|
const [, , ...argv] = process.argv;
|
|
854
1068
|
const cmd = argv[0];
|
|
855
1069
|
if (cmd === "-h" || cmd === "--help" || cmd === "help") printHelp();
|
|
@@ -860,20 +1074,5 @@ else if (cmd === "doctor") await cmdDoctor();
|
|
|
860
1074
|
else if (cmd === "--version" || cmd === "-v") console.log(`omnirush ${ownVersion()}`);
|
|
861
1075
|
else if (cmd === "collect") await cmdCollect(argv.slice(1));
|
|
862
1076
|
else if (cmd === undefined || cmd === "run") await cmdRun();
|
|
863
|
-
else
|
|
864
|
-
|
|
865
|
-
// `--provider x`) to pi, defaulting the model the same way
|
|
866
|
-
// `omnirush run` does; user-specified --provider/--model are never
|
|
867
|
-
// duplicated and pi admin subcommands pass through untouched.
|
|
868
|
-
ensureDirs();
|
|
869
|
-
const auth = requireAuth();
|
|
870
|
-
installModelsJson(agentGatewayUrl(auth), await launchModels(agentGatewayUrl(auth), auth.accessToken));
|
|
871
|
-
installExtensions();
|
|
872
|
-
const entry = piEntry();
|
|
873
|
-
const { cmd: bin, args } = await runtimeFor(entry);
|
|
874
|
-
const piArgs = withModelDefaults(argv, resolveModelDefaults(process.env));
|
|
875
|
-
spawnAgent(bin, [...args, ...piArgs], {
|
|
876
|
-
stdio: "inherit",
|
|
877
|
-
env: { ...process.env, ...childEnv(auth) },
|
|
878
|
-
});
|
|
879
|
-
}
|
|
1077
|
+
else if (cmd === "update") await cmdUpdate(argv.slice(1));
|
|
1078
|
+
else await runCore(argv);
|