omnirush 0.3.3 → 0.4.2
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/collect-once.ts +77 -39
- package/assets/extensions/omnirush/auth.js +56 -17
- package/assets/extensions/omnirush/collector-lib.ts +510 -201
- 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/retry.js +144 -0
- package/assets/extensions/omnirush/sota-lib.ts +64 -0
- package/assets/extensions/omnirush/sota.ts +159 -28
- 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/scripts/postinstall.js +25 -8
- package/src/bin.js +299 -46
- package/src/lib.js +13 -0
|
@@ -42,10 +42,18 @@ export default function (pi: any) {
|
|
|
42
42
|
return ok ? refresher.auth?.accessToken ?? null : null;
|
|
43
43
|
}
|
|
44
44
|
: undefined,
|
|
45
|
-
log: (level, message) => {
|
|
45
|
+
log: (level, message, attributes) => {
|
|
46
46
|
if (level === "warn") {
|
|
47
47
|
try {
|
|
48
|
-
|
|
48
|
+
// Attributes carry the diagnosis (HTTP status, error message,
|
|
49
|
+
// path, byte counts) — without them a remote failure is
|
|
50
|
+
// undebuggable ("collection operation failed" and nothing
|
|
51
|
+
// else). Attribute values are telemetry-safe (ids, sizes,
|
|
52
|
+
// error strings; never tokens).
|
|
53
|
+
const detail = attributes && Object.keys(attributes).length > 0
|
|
54
|
+
? ` ${JSON.stringify(attributes)}`
|
|
55
|
+
: "";
|
|
56
|
+
process.stderr.write(`omnirush collector: ${message}${detail}\n`);
|
|
49
57
|
} catch {
|
|
50
58
|
/* stderr may be closed late in shutdown */
|
|
51
59
|
}
|
|
@@ -1,48 +1,173 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Omnirush commands:
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* Omnirush commands:
|
|
3
|
+
* /grant today's token grant + usage (headers when a request ran,
|
|
4
|
+
* /device/me otherwise)
|
|
5
|
+
* /usage full usage from the manager (grant date, token counters,
|
|
6
|
+
* devices)
|
|
7
|
+
* /status one-screen session summary (identity, model/effort, grant,
|
|
8
|
+
* collector, origin, version)
|
|
9
|
+
* /verify prove the served model is genuine — full implementation
|
|
10
|
+
* lands with the gateway's model-attestation endpoint (M2)
|
|
11
|
+
* plus the footer status line that keeps grant/model info visible while
|
|
12
|
+
* the agent runs.
|
|
13
|
+
*
|
|
14
|
+
* Handler contract (pi): handler(args: string, ctx: ExtensionCommandContext)
|
|
15
|
+
* — the FIRST parameter is the argument string, ctx comes SECOND. Getting
|
|
16
|
+
* this wrong silently no-ops the command (ctx.ui is undefined on a string),
|
|
17
|
+
* which is exactly how /grant and /verify broke the first time.
|
|
6
18
|
*/
|
|
7
19
|
|
|
20
|
+
import { readSessionLedger, SESSION_LEDGER_FILE } from "./collector-lib";
|
|
21
|
+
import { deviceMe, omniDir, resolveOrigin } from "./auth";
|
|
22
|
+
import { sharedRefresher } from "./refresh";
|
|
23
|
+
import { formatCollectorLine, formatStatusLines } from "./status-lib";
|
|
24
|
+
import {
|
|
25
|
+
formatDeviceLines,
|
|
26
|
+
formatGrantLines,
|
|
27
|
+
formatGrantStatusLine,
|
|
28
|
+
formatTokenCount,
|
|
29
|
+
formatUsageDetails,
|
|
30
|
+
formatUsageSummary,
|
|
31
|
+
gatewayUsage,
|
|
32
|
+
grantFromUsage,
|
|
33
|
+
} from "./usage";
|
|
34
|
+
|
|
8
35
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
9
|
-
import { deviceMe, resolveOrigin } from "./auth";
|
|
10
|
-
import { formatGrantLines, formatGrantStatusLine, gatewayUsage } from "./usage";
|
|
11
36
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
37
|
+
const CLI_VERSION = (process.env.OMNIRUSH_VERSION || "").trim() || "unknown";
|
|
38
|
+
|
|
39
|
+
interface MeResult {
|
|
40
|
+
me: any;
|
|
41
|
+
error?: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Fetch the signed-in identity + server-side usage with the broker
|
|
46
|
+
* pattern: 401 triggers one single-flight refresh (both tokens rotate,
|
|
47
|
+
* persisted 0600) and a single retry with the rotated token. Never
|
|
48
|
+
* throws; errors come back as `error` strings safe to display.
|
|
49
|
+
*/
|
|
50
|
+
async function fetchMe(): Promise<MeResult> {
|
|
51
|
+
const origin = resolveOrigin(process.env);
|
|
52
|
+
const refresher = sharedRefresher();
|
|
53
|
+
// Prefer the refresher's live token — it is already rotated after any
|
|
54
|
+
// in-flight 401 recovery — and fall back to the launcher-provided one.
|
|
55
|
+
const token = refresher.auth?.accessToken || (process.env.OMNIRUSH_TOKEN || "").trim();
|
|
56
|
+
if (!token) return { me: null, error: "not signed in — run `omnirush login` first" };
|
|
16
57
|
try {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
58
|
+
let me = await deviceMe(origin, { accessToken: token, fetchImpl: globalThis.fetch });
|
|
59
|
+
if (me !== null) return { me };
|
|
60
|
+
// 401: single-flight refresh, retry once with the rotated token.
|
|
61
|
+
const refreshed = await refresher.refresh(token).catch(() => false);
|
|
62
|
+
const next = refresher.auth?.accessToken;
|
|
63
|
+
if (refreshed && next && next !== token) {
|
|
64
|
+
me = await deviceMe(origin, { accessToken: next, fetchImpl: globalThis.fetch });
|
|
65
|
+
if (me !== null) return { me };
|
|
66
|
+
}
|
|
67
|
+
return { me: null, error: "credentials rejected — run `omnirush login` again" };
|
|
68
|
+
} catch (error) {
|
|
69
|
+
return { me: null, error: `manager unreachable (${error?.message ?? error})` };
|
|
23
70
|
}
|
|
24
71
|
}
|
|
25
72
|
|
|
73
|
+
function identityOf(me: any): string | null {
|
|
74
|
+
if (!me || typeof me !== "object") return null;
|
|
75
|
+
for (const key of ["email", "device_name", "user_id", "id"] as const) {
|
|
76
|
+
const value = me[key];
|
|
77
|
+
if (typeof value === "string" && value) return value;
|
|
78
|
+
}
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** The grant summary string shared by /grant and /status, or null. */
|
|
83
|
+
function grantSummary(me: any): { text: string; date?: string } | null {
|
|
84
|
+
const usage = gatewayUsage();
|
|
85
|
+
const fromHeaders = grantFromUsage(usage);
|
|
86
|
+
const meUsage = me?.usage ?? null;
|
|
87
|
+
const limit = fromHeaders?.limitTokens ?? Number(meUsage?.token_limit);
|
|
88
|
+
const remaining = fromHeaders?.remainingTokens ?? Number(meUsage?.remaining_tokens);
|
|
89
|
+
if (!Number.isFinite(limit) || !Number.isFinite(remaining)) return null;
|
|
90
|
+
const used = Number(meUsage?.used_tokens);
|
|
91
|
+
const usedPart = Number.isFinite(used) ? ` (used ${formatTokenCount(used)})` : "";
|
|
92
|
+
return {
|
|
93
|
+
text: `${formatTokenCount(remaining)} of ${formatTokenCount(limit)} tokens remaining${usedPart}`,
|
|
94
|
+
date: typeof meUsage?.grant_date === "string" ? meUsage.grant_date : undefined,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function notify(ctx: any, text: string, level: "info" | "error" = "info"): void {
|
|
99
|
+
ctx?.ui?.notify?.(text, level);
|
|
100
|
+
}
|
|
101
|
+
|
|
26
102
|
export default function (pi: ExtensionAPI) {
|
|
27
103
|
pi.registerCommand("grant", {
|
|
28
104
|
description: "Show today's Omnirush token grant and usage",
|
|
29
|
-
handler: async (ctx: any) => {
|
|
105
|
+
handler: async (_args: string, ctx: any) => {
|
|
30
106
|
const usage = gatewayUsage();
|
|
31
|
-
const me = await
|
|
107
|
+
const { me } = await fetchMe();
|
|
32
108
|
const sessionId = ctx?.sessionManager?.getSessionId?.() ?? undefined;
|
|
33
109
|
const lines = formatGrantLines({ usage, me, sessionId });
|
|
34
|
-
ctx.
|
|
35
|
-
|
|
36
|
-
|
|
110
|
+
notify(ctx, ["Omnirush usage:", ...lines.map((line) => ` ${line}`)].join("\n"));
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
pi.registerCommand("usage", {
|
|
115
|
+
description: "Show detailed Omnirush usage from the manager (counters + devices)",
|
|
116
|
+
handler: async (_args: string, ctx: any) => {
|
|
117
|
+
const { me, error } = await fetchMe();
|
|
118
|
+
if (!me) {
|
|
119
|
+
notify(ctx, `Omnirush usage unavailable: ${error}`, "error");
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const lines = [
|
|
123
|
+
"Omnirush usage (from the manager):",
|
|
124
|
+
...formatUsageDetails({ me }).map((line) => ` ${line}`),
|
|
125
|
+
];
|
|
126
|
+
const summary = formatUsageSummary(me);
|
|
127
|
+
if (summary) lines.push(` summary: ${summary}`);
|
|
128
|
+
lines.push("", ...formatDeviceLines(me));
|
|
129
|
+
notify(ctx, lines.join("\n"));
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
pi.registerCommand("status", {
|
|
134
|
+
description: "Show the Omnirush session status (identity, model, grant, collector)",
|
|
135
|
+
handler: async (_args: string, ctx: any) => {
|
|
136
|
+
const { me } = await fetchMe();
|
|
137
|
+
const usage = gatewayUsage();
|
|
138
|
+
const grant = grantSummary(me);
|
|
139
|
+
const sessionId = String(ctx?.sessionManager?.getSessionId?.() ?? "");
|
|
140
|
+
let record = null;
|
|
141
|
+
try {
|
|
142
|
+
const ledger = await readSessionLedger(`${omniDir()}/${SESSION_LEDGER_FILE}`);
|
|
143
|
+
record = ledger.sessions[sessionId] ?? null;
|
|
144
|
+
} catch {
|
|
145
|
+
/* collector state is best effort */
|
|
146
|
+
}
|
|
147
|
+
const model = ctx?.model;
|
|
148
|
+
const statusLines = formatStatusLines({
|
|
149
|
+
signedIn: Boolean(me),
|
|
150
|
+
identity: identityOf(me),
|
|
151
|
+
model: model ? `${model.provider ?? "omnirush"}/${model.id}` : null,
|
|
152
|
+
thinkingLevel: typeof ctx?.thinkingLevel === "string" ? ctx.thinkingLevel : null,
|
|
153
|
+
effort: usage?.["x-omnirush-effort"] ?? null,
|
|
154
|
+
grant: grant?.text ?? null,
|
|
155
|
+
grantDate: grant?.date ?? null,
|
|
156
|
+
collector: { sessionId, record },
|
|
157
|
+
origin: resolveOrigin(process.env),
|
|
158
|
+
version: CLI_VERSION,
|
|
159
|
+
});
|
|
160
|
+
notify(ctx, ["Omnirush status:", ...statusLines.map((line) => ` ${line}`)].join("\n"));
|
|
37
161
|
},
|
|
38
162
|
});
|
|
39
163
|
|
|
40
164
|
pi.registerCommand("verify", {
|
|
41
165
|
description: "Verify the model serving your requests is genuine GPT-6 Astra",
|
|
42
|
-
handler: async (ctx: any) => {
|
|
166
|
+
handler: async (_args: string, ctx: any) => {
|
|
43
167
|
// M2: POST /omnirush/verify → signed attestation compared against
|
|
44
168
|
// observed model behavior. v0 placeholder.
|
|
45
|
-
|
|
169
|
+
notify(
|
|
170
|
+
ctx,
|
|
46
171
|
"verify: model attestation arrives with the gateway lane (M2).",
|
|
47
172
|
);
|
|
48
173
|
},
|
|
@@ -3,10 +3,13 @@
|
|
|
3
3
|
// pi discovers either extensions/*.ts files or extensions/<dir>/index.ts —
|
|
4
4
|
// this index makes the whole omnirush extension directory load as one
|
|
5
5
|
// extension (without it, none of these files were ever registered).
|
|
6
|
+
// EVERY module that registers commands/tools/handlers must be wired here:
|
|
7
|
+
// a module left out of this list is silently dead in sessions.
|
|
6
8
|
|
|
7
9
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
8
10
|
import collector from "./collector";
|
|
9
11
|
import commands from "./commands";
|
|
12
|
+
import mcp from "./mcp";
|
|
10
13
|
import recorder from "./recorder";
|
|
11
14
|
import sota from "./sota";
|
|
12
15
|
|
|
@@ -15,4 +18,5 @@ export default function (pi: ExtensionAPI) {
|
|
|
15
18
|
recorder(pi as any);
|
|
16
19
|
sota(pi as any);
|
|
17
20
|
collector(pi as any);
|
|
21
|
+
mcp(pi as any);
|
|
18
22
|
}
|