omnirush 0.3.3 → 0.4.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/assets/extensions/omnirush/collector-lib.ts +64 -29
- package/assets/extensions/omnirush/collector.ts +10 -2
- package/assets/extensions/omnirush/commands.ts +148 -23
- package/assets/extensions/omnirush/index.ts +4 -0
- package/assets/extensions/omnirush/mcp-client.bundle.js +19432 -0
- package/assets/extensions/omnirush/mcp-lib.ts +199 -0
- package/assets/extensions/omnirush/mcp.ts +379 -0
- package/assets/extensions/omnirush/status-lib.ts +85 -0
- package/assets/extensions/omnirush/usage.ts +77 -0
- package/package.json +7 -2
- package/scripts/build-mcp-bundle.js +47 -0
- package/scripts/patch-pi-branding.js +27 -0
- package/src/bin.js +46 -4
|
@@ -151,6 +151,83 @@ export function formatGrantLines({
|
|
|
151
151
|
return lines;
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
+
/**
|
|
155
|
+
* Exact ints for /usage, rendered with both the compact human form and the
|
|
156
|
+
* full digits (the full number is what support asks for).
|
|
157
|
+
*/
|
|
158
|
+
function tokenLine(label: string, value: unknown): string | null {
|
|
159
|
+
const n = intOr(value);
|
|
160
|
+
if (n === null) return null;
|
|
161
|
+
return `${label}: ${formatTokenCount(n)} (${n.toLocaleString("en-US")})`;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* The detailed /usage block from a /device/me payload: grant date, token
|
|
166
|
+
* limit, used/reserved/remaining. Every field is optional server-side
|
|
167
|
+
* (early in a grant day only grant_date may be present), so lines appear
|
|
168
|
+
* only when the data exists.
|
|
169
|
+
*/
|
|
170
|
+
export function formatUsageDetails({ me }: { me: any }): string[] {
|
|
171
|
+
const u = me?.usage ?? null;
|
|
172
|
+
if (!u || typeof u !== "object") return ["no usage reported by the manager yet"];
|
|
173
|
+
const lines: string[] = [];
|
|
174
|
+
if (u.grant_date) lines.push(`grant date: ${u.grant_date}`);
|
|
175
|
+
let counters = 0;
|
|
176
|
+
for (const [label, key] of [
|
|
177
|
+
["token limit", "token_limit"],
|
|
178
|
+
["used", "used_tokens"],
|
|
179
|
+
["reserved", "reserved_tokens"],
|
|
180
|
+
["remaining", "remaining_tokens"],
|
|
181
|
+
] as const) {
|
|
182
|
+
const line = tokenLine(label, u[key]);
|
|
183
|
+
if (line) {
|
|
184
|
+
lines.push(line);
|
|
185
|
+
counters += 1;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if (counters === 0) {
|
|
189
|
+
lines.push("no token counters reported yet (they appear with the first settled request)");
|
|
190
|
+
}
|
|
191
|
+
return lines;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* One-line /usage summary, e.g.
|
|
196
|
+
* "997.2M of 1.0B tokens remaining (used 2.8M) — 2026-09-23".
|
|
197
|
+
* Falls back to whatever subset is present; empty string with no data.
|
|
198
|
+
*/
|
|
199
|
+
export function formatUsageSummary(me: any): string {
|
|
200
|
+
const u = me?.usage ?? null;
|
|
201
|
+
if (!u || typeof u !== "object") return "";
|
|
202
|
+
const limit = intOr(u.token_limit);
|
|
203
|
+
const used = intOr(u.used_tokens);
|
|
204
|
+
const reserved = intOr(u.reserved_tokens);
|
|
205
|
+
const remaining = intOr(u.remaining_tokens);
|
|
206
|
+
const parts: string[] = [];
|
|
207
|
+
if (remaining !== null && limit !== null) {
|
|
208
|
+
parts.push(`${formatTokenCount(remaining)} of ${formatTokenCount(limit)} tokens remaining`);
|
|
209
|
+
} else if (used !== null) {
|
|
210
|
+
parts.push(`${formatTokenCount(used)} tokens used`);
|
|
211
|
+
}
|
|
212
|
+
if (reserved !== null && reserved > 0) parts.push(`${formatTokenCount(reserved)} reserved`);
|
|
213
|
+
if (u.grant_date) parts.push(String(u.grant_date));
|
|
214
|
+
return parts.join(" — ");
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/** Device list lines for /usage and /status (no credentials in me payloads). */
|
|
218
|
+
export function formatDeviceLines(me: any): string[] {
|
|
219
|
+
const devices = Array.isArray(me?.devices) ? me.devices : [];
|
|
220
|
+
if (devices.length === 0) return [];
|
|
221
|
+
const lines = devices.map((device: any, index: number) => {
|
|
222
|
+
const name = typeof device?.device_name === "string" ? device.device_name : "device";
|
|
223
|
+
const platform = typeof device?.platform === "string" ? ` (${device.platform})` : "";
|
|
224
|
+
const seen = typeof device?.last_seen_at === "string" ? ` last seen ${device.last_seen_at}` : "";
|
|
225
|
+
const revoked = device?.revoked_at ? " [revoked]" : "";
|
|
226
|
+
return ` ${index + 1}. ${name}${platform}${seen}${revoked}`;
|
|
227
|
+
});
|
|
228
|
+
return [`devices signed in: ${devices.length}`, ...lines];
|
|
229
|
+
}
|
|
230
|
+
|
|
154
231
|
/** One-line footer summary for the TUI status area, or null with no data. */
|
|
155
232
|
export function formatGrantStatusLine(
|
|
156
233
|
usage: Record<string, string> | null,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "omnirush",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "Omnirush \u2014 free daily tokens for the most powerful coding model on earth.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"scripts": {
|
|
14
14
|
"test": "node --test",
|
|
15
15
|
"test:bun": "if [ -x .runtime/bin/bun ]; then .runtime/bin/bun test test/; else bun test test/; fi",
|
|
16
|
+
"build:mcp": "node scripts/build-mcp-bundle.js",
|
|
16
17
|
"postinstall": "node scripts/patch-pi-branding.js && node scripts/postinstall.js"
|
|
17
18
|
},
|
|
18
19
|
"dependencies": {
|
|
@@ -32,5 +33,9 @@
|
|
|
32
33
|
"assets",
|
|
33
34
|
"scripts",
|
|
34
35
|
"README.md"
|
|
35
|
-
]
|
|
36
|
+
],
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
39
|
+
"esbuild": "^0.28.2"
|
|
40
|
+
}
|
|
36
41
|
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Bundle the MCP client SDK (@modelcontextprotocol/sdk) into a single
|
|
3
|
+
// self-contained ESM file committed at
|
|
4
|
+
// assets/extensions/omnirush/mcp-client.bundle.js.
|
|
5
|
+
//
|
|
6
|
+
// Why a committed bundle: the omnirush extension directory is copied to
|
|
7
|
+
// ~/.pi/agent/extensions/omnirush/ and its files can only import
|
|
8
|
+
// siblings — the jiti loader that runs pi extensions has no npm
|
|
9
|
+
// resolution there, and the published npm package ships no node_modules.
|
|
10
|
+
// The bundle ships in the repo, so CI and `npm pack` never need the
|
|
11
|
+
// toolchain; regenerate only when bumping the SDK:
|
|
12
|
+
//
|
|
13
|
+
// npm run build:mcp
|
|
14
|
+
//
|
|
15
|
+
// The output is deterministic for a given SDK version (esbuild, fixed
|
|
16
|
+
// entry, no code splitting) so diffs stay reviewable.
|
|
17
|
+
|
|
18
|
+
import { build } from "esbuild";
|
|
19
|
+
import path from "node:path";
|
|
20
|
+
import { fileURLToPath } from "node:url";
|
|
21
|
+
|
|
22
|
+
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
23
|
+
const entry = path.join(root, "dev", "mcp-bundle-entry.js");
|
|
24
|
+
const outfile = path.join(root, "assets", "extensions", "omnirush", "mcp-client.bundle.js");
|
|
25
|
+
|
|
26
|
+
await build({
|
|
27
|
+
entryPoints: [entry],
|
|
28
|
+
outfile,
|
|
29
|
+
bundle: true,
|
|
30
|
+
platform: "node",
|
|
31
|
+
format: "esm",
|
|
32
|
+
target: "node18",
|
|
33
|
+
// Node builtins stay external (real `node:*` imports in the output);
|
|
34
|
+
// everything else — including cross-spawn — is inlined.
|
|
35
|
+
external: ["node:*"],
|
|
36
|
+
minify: false,
|
|
37
|
+
sourcemap: false,
|
|
38
|
+
legalComments: "none",
|
|
39
|
+
logLevel: "info",
|
|
40
|
+
banner: {
|
|
41
|
+
js: [
|
|
42
|
+
"// GENERATED FILE — do not edit by hand.",
|
|
43
|
+
"// Regenerate with: npm run build:mcp",
|
|
44
|
+
"// Bundled from @modelcontextprotocol/sdk (devDependency) via esbuild.",
|
|
45
|
+
].join("\n"),
|
|
46
|
+
},
|
|
47
|
+
});
|
|
@@ -93,6 +93,33 @@ export const REPLACEMENTS = [
|
|
|
93
93
|
from: "Update pi only (self works as alias to pi)",
|
|
94
94
|
to: "Update the agent only (self works as an alias)",
|
|
95
95
|
},
|
|
96
|
+
// Belt-and-braces against the pi.dev update banner (Foxtran's 0.73.1
|
|
97
|
+
// report: a stale core showed "Update Available — pi.dev/changelog"
|
|
98
|
+
// even though PI_OFFLINE should suppress the check). PI_OFFLINE=1 in
|
|
99
|
+
// the spawn env stops the check; these rewrites make the banner
|
|
100
|
+
// UNRENDERABLE even where PI_OFFLINE is absent (direct pi spawns,
|
|
101
|
+
// older cores): showNewVersionNotification returns before its body
|
|
102
|
+
// (the parameter is renamed so the replacement stays idempotent and
|
|
103
|
+
// never re-matches), and the pi.dev changelog URL is blanked. The
|
|
104
|
+
// parameter spelling differs between the standalone dist (readable)
|
|
105
|
+
// and the minified runtime bundle chunk, hence the two variants; a
|
|
106
|
+
// future core that renames the parameter just reports "missing"
|
|
107
|
+
// (fail-soft) and we would re-derive the target.
|
|
108
|
+
{
|
|
109
|
+
label: "TUI update banner (standalone dist)",
|
|
110
|
+
from: "showNewVersionNotification(release) {",
|
|
111
|
+
to: "showNewVersionNotification(_release) { return;",
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
label: "TUI update banner (runtime bundle chunk)",
|
|
115
|
+
from: "showNewVersionNotification(release2){",
|
|
116
|
+
to: "showNewVersionNotification(_r){return;",
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
label: "pi.dev changelog URL",
|
|
120
|
+
from: '"https://pi.dev/changelog"',
|
|
121
|
+
to: '""',
|
|
122
|
+
},
|
|
96
123
|
];
|
|
97
124
|
|
|
98
125
|
function findPiRoot(startDir) {
|
package/src/bin.js
CHANGED
|
@@ -79,7 +79,7 @@ function managerOrigin() {
|
|
|
79
79
|
);
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
function
|
|
82
|
+
function piPackage() {
|
|
83
83
|
// The pi package is Bun-first and its "exports" map blocks plain Node
|
|
84
84
|
// resolution, so locate it by path: walk up from our own package root
|
|
85
85
|
// to the nearest node_modules containing it.
|
|
@@ -88,13 +88,22 @@ function piEntry() {
|
|
|
88
88
|
for (let i = 0; i < 6; i++) {
|
|
89
89
|
const pkgPath = path.join(dir, "node_modules", PKG, "package.json");
|
|
90
90
|
if (fs.existsSync(pkgPath)) {
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
try {
|
|
92
|
+
return { dir: path.dirname(pkgPath), pkg: JSON.parse(fs.readFileSync(pkgPath, "utf8")) };
|
|
93
|
+
} catch {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
93
96
|
}
|
|
94
97
|
const parent = path.dirname(dir);
|
|
95
98
|
if (parent === dir) break;
|
|
96
99
|
dir = parent;
|
|
97
100
|
}
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function piEntry() {
|
|
105
|
+
const found = piPackage();
|
|
106
|
+
if (found?.pkg?.bin?.pi) return path.join(found.dir, found.pkg.bin.pi);
|
|
98
107
|
throw new Error("could not locate the pinned pi CLI (@earendil-works/pi-coding-agent)");
|
|
99
108
|
}
|
|
100
109
|
|
|
@@ -421,6 +430,24 @@ async function cmdDoctor() {
|
|
|
421
430
|
}
|
|
422
431
|
ok("agent config", true, PI_MODELS_JSON);
|
|
423
432
|
ok("extensions", true, PI_EXT_DIR);
|
|
433
|
+
// Agent core version: a stale core predates the omnirush provider and
|
|
434
|
+
// branding and shows pi.dev "Update Available" banners (Foxtran's
|
|
435
|
+
// 0.73.1 report). Advisory WARN with the fix; exit code unaffected.
|
|
436
|
+
{
|
|
437
|
+
const found = piPackage();
|
|
438
|
+
const coreVersion = String(found?.pkg?.version ?? "");
|
|
439
|
+
const own = ownVersion();
|
|
440
|
+
if (!coreVersion) {
|
|
441
|
+
console.log(`WARN agent core — pinned agent package not found (broken install). Fix: omnirush update`);
|
|
442
|
+
} else if (coreVersion === own) {
|
|
443
|
+
ok("agent core", true, `v${coreVersion}`);
|
|
444
|
+
} else {
|
|
445
|
+
console.log(
|
|
446
|
+
`WARN agent core — installed core is v${coreVersion}, this omnirush expects v${own} ` +
|
|
447
|
+
"(stale install: pi.dev update banners, missing omnirush provider). Fix: omnirush update",
|
|
448
|
+
);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
424
451
|
// Search tools: bundled fd/rg (or system ones) keep the agent off its
|
|
425
452
|
// GitHub download path, which fails on restricted networks.
|
|
426
453
|
const toolsRoot = path.join(PI_AGENT_DIR, "bin");
|
|
@@ -465,6 +492,14 @@ function requireAuth() {
|
|
|
465
492
|
process.exit(1);
|
|
466
493
|
}
|
|
467
494
|
|
|
495
|
+
function ownVersion() {
|
|
496
|
+
try {
|
|
497
|
+
return JSON.parse(fs.readFileSync(path.join(__dirname, "..", "package.json"), "utf8")).version;
|
|
498
|
+
} catch {
|
|
499
|
+
return "unknown";
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
468
503
|
function childEnv(auth) {
|
|
469
504
|
const gateway = agentGatewayUrl(auth);
|
|
470
505
|
const origin = (process.env.OMNIRUSH_ORIGIN || "").trim().replace(/\/+$/, "") ||
|
|
@@ -475,12 +510,19 @@ function childEnv(auth) {
|
|
|
475
510
|
OMNIRUSH_ORIGIN: origin,
|
|
476
511
|
OMNIRUSH_GATEWAY_URL: gateway,
|
|
477
512
|
OMNIRUSH_DIR: OMNI_DIR,
|
|
513
|
+
// /status shows the CLI version; extensions have no package.json of
|
|
514
|
+
// their own in ~/.pi, so the launcher hands it over.
|
|
515
|
+
OMNIRUSH_VERSION: ownVersion(),
|
|
478
516
|
// The agent resolves its config dir from <APP>_CODING_AGENT_DIR. The
|
|
479
517
|
// branding patch renames the app to omnirush, so set the omnirush
|
|
480
518
|
// spelling; the pi spelling stays for unpatched cores.
|
|
481
519
|
OMNIRUSH_CODING_AGENT_DIR: PI_AGENT_DIR,
|
|
482
520
|
PI_CODING_AGENT_DIR: PI_AGENT_DIR,
|
|
483
521
|
// The agent core is a fork/rebrand: never phone home to pi.dev.
|
|
522
|
+
// PI_OFFLINE kills startup update checks (the "Update Available —
|
|
523
|
+
// pi.dev/changelog" banner), package updates, and install/update
|
|
524
|
+
// telemetry; PI_TELEMETRY stays for cores that predate PI_OFFLINE.
|
|
525
|
+
PI_OFFLINE: "1",
|
|
484
526
|
PI_TELEMETRY: "0",
|
|
485
527
|
};
|
|
486
528
|
}
|
|
@@ -527,7 +569,7 @@ if (cmd === "login") await cmdLogin(argv.slice(1));
|
|
|
527
569
|
else if (cmd === "logout") await cmdLogout();
|
|
528
570
|
else if (cmd === "whoami") await cmdWhoami();
|
|
529
571
|
else if (cmd === "doctor") await cmdDoctor();
|
|
530
|
-
else if (cmd === "--version" || cmd === "-v") console.log(
|
|
572
|
+
else if (cmd === "--version" || cmd === "-v") console.log(`omnirush ${ownVersion()}`);
|
|
531
573
|
else if (cmd === "collect") await cmdCollect(argv.slice(1));
|
|
532
574
|
else if (cmd === undefined || cmd === "run") await cmdRun();
|
|
533
575
|
else {
|