omnirush 0.10.4 → 0.10.5
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.
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
import { readSessionLedger, SESSION_LEDGER_FILE } from "./capture/workspace-sync";
|
|
21
21
|
import { deviceMe, omniDir, resolveOrigin, sessionEndedMessage } from "./auth";
|
|
22
22
|
import { sharedRefresher } from "./refresh";
|
|
23
|
-
import {
|
|
23
|
+
import { formatStatusLines } from "./status-lib";
|
|
24
24
|
import { formatModeStatus } from "./yolo-lib";
|
|
25
25
|
import {
|
|
26
26
|
formatDeviceLines,
|
|
@@ -129,7 +129,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
129
129
|
});
|
|
130
130
|
|
|
131
131
|
pi.registerCommand("status", {
|
|
132
|
-
description: "Show the Omnirush session status (identity, model, grant
|
|
132
|
+
description: "Show the Omnirush session status (identity, model, grant)",
|
|
133
133
|
handler: async (_args: string, ctx: any) => {
|
|
134
134
|
const { me } = await fetchMe();
|
|
135
135
|
const usage = gatewayUsage();
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
|
|
47
47
|
import { createHash } from "node:crypto";
|
|
48
48
|
import markSubagentSession from "./subagent-marker";
|
|
49
|
-
import { createReadStream, readdirSync, readFileSync, rmSync } from "node:fs";
|
|
49
|
+
import { appendFileSync, createReadStream, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, statSync } from "node:fs";
|
|
50
50
|
import { readdir, stat } from "node:fs/promises";
|
|
51
51
|
import os from "node:os";
|
|
52
52
|
import path from "node:path";
|
|
@@ -483,10 +483,27 @@ export default function (pi: any) {
|
|
|
483
483
|
const version = cliVersion();
|
|
484
484
|
const engineVersion = (process.env.OMNIRUSH_ENGINE_VERSION || "").trim();
|
|
485
485
|
const verbose = (process.env.OMNIRUSH_DEBUG || "").trim() === "1";
|
|
486
|
+
// Upload diagnostics never reach the terminal (they interleave with the
|
|
487
|
+
// TUI): warnings go to <state dir>/logs/session-upload.log (kept under
|
|
488
|
+
// ~1 MB), and OMNIRUSH_DEBUG=1 also prints everything to stderr.
|
|
489
|
+
const logFile = path.join(stateDir, "logs", "session-upload.log");
|
|
486
490
|
const log = (level: "info" | "warn", message: string, attributes?: Record<string, unknown>) => {
|
|
487
491
|
if (level !== "warn" && !verbose) return;
|
|
492
|
+
const detail = attributes && Object.keys(attributes).length > 0 ? ` ${JSON.stringify(attributes)}` : "";
|
|
493
|
+
const line = `${new Date().toISOString()} ${level} ${message.replace(/^OmniRush session upload: /, "")}${detail}\n`;
|
|
494
|
+
try {
|
|
495
|
+
mkdirSync(path.dirname(logFile), { recursive: true });
|
|
496
|
+
try {
|
|
497
|
+
if (statSync(logFile).size > 1_000_000) renameSync(logFile, `${logFile}.1`);
|
|
498
|
+
} catch {
|
|
499
|
+
/* no log yet */
|
|
500
|
+
}
|
|
501
|
+
appendFileSync(logFile, line);
|
|
502
|
+
} catch {
|
|
503
|
+
/* diagnostics only */
|
|
504
|
+
}
|
|
505
|
+
if (!verbose) return;
|
|
488
506
|
try {
|
|
489
|
-
const detail = attributes && Object.keys(attributes).length > 0 ? ` ${JSON.stringify(attributes)}` : "";
|
|
490
507
|
process.stderr.write(`omnirush session upload: ${message}${detail}\n`);
|
|
491
508
|
} catch {
|
|
492
509
|
/* stderr may be closed late in shutdown */
|
|
@@ -80,7 +80,6 @@ export function formatStatusLines(input: StatusInput): string[] {
|
|
|
80
80
|
lines.push("grant: no data yet (appears after the first request or via /usage)");
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
lines.push(formatSyncLine(input.sync));
|
|
84
83
|
if (input.yolo) lines.push(input.yolo);
|
|
85
84
|
lines.push(`manager: ${input.origin}`);
|
|
86
85
|
lines.push(`version: ${input.version}`);
|
package/package.json
CHANGED
package/scripts/brand-engine.js
CHANGED
|
@@ -57,6 +57,29 @@ export const CORE_ENV_PATTERN = `/^PI_(${CORE_ONLY_ENV_NAMES.join("|")})$/`;
|
|
|
57
57
|
// `from` must match the installed dist exactly; applied with replaceAll to
|
|
58
58
|
// every dist/**/*.js. A `to` must never contain its own `from` (idempotency).
|
|
59
59
|
export const REPLACEMENTS = [
|
|
60
|
+
// --- app name fallback: omnirush even when the core can't read piConfig ---
|
|
61
|
+
// (a package dir resolved elsewhere, e.g. a bun binary or a moved folder).
|
|
62
|
+
// This is the name in "To resume this session: <name> --session <id>".
|
|
63
|
+
{
|
|
64
|
+
label: "app name fallback (bundle)",
|
|
65
|
+
from: 'APP_NAME=piConfigName||"pi"',
|
|
66
|
+
to: 'APP_NAME=piConfigName||"omnirush"',
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
label: "app name fallback (dist)",
|
|
70
|
+
from: 'APP_NAME = piConfigName || "pi"',
|
|
71
|
+
to: 'APP_NAME = piConfigName || "omnirush"',
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
label: "app title fallback (bundle)",
|
|
75
|
+
from: 'APP_TITLE=piConfigName?APP_NAME:"\\u03C0"',
|
|
76
|
+
to: 'APP_TITLE=APP_NAME',
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
label: "app title fallback (dist)",
|
|
80
|
+
from: 'APP_TITLE = piConfigName ? APP_NAME : "\u03c0"',
|
|
81
|
+
to: 'APP_TITLE = APP_NAME',
|
|
82
|
+
},
|
|
60
83
|
// --- project config folder: legacy <project>/.pi fallback -------------
|
|
61
84
|
{
|
|
62
85
|
label: "project config dir hook (bundle)",
|
|
@@ -450,6 +473,23 @@ export const REPLACEMENTS = [
|
|
|
450
473
|
},
|
|
451
474
|
];
|
|
452
475
|
|
|
476
|
+
// Replace a file in one step: the launcher may brand on first run while
|
|
477
|
+
// another omnirush start is loading the same core files.
|
|
478
|
+
function writeAtomic(file, data) {
|
|
479
|
+
const tmp = `${file}.${process.pid}.branding`;
|
|
480
|
+
try {
|
|
481
|
+
fs.writeFileSync(tmp, data);
|
|
482
|
+
fs.renameSync(tmp, file);
|
|
483
|
+
} catch (error) {
|
|
484
|
+
try {
|
|
485
|
+
fs.rmSync(tmp, { force: true });
|
|
486
|
+
} catch {
|
|
487
|
+
/* nothing to clean */
|
|
488
|
+
}
|
|
489
|
+
throw error;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
453
493
|
function findPiRoot(startDir) {
|
|
454
494
|
let dir = startDir;
|
|
455
495
|
for (let i = 0; i < 8; i++) {
|
|
@@ -485,6 +525,15 @@ export function applyBrandingPatches(startDir, { version } = {}) {
|
|
|
485
525
|
if (!piRoot) {
|
|
486
526
|
return { piRoot: null, packageJsonPatched: false, applied: [], missing: [{ label: "core", reason: `${PKG} not installed` }] };
|
|
487
527
|
}
|
|
528
|
+
return brandCoreDir(piRoot, { version });
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* Apply the rebrand to the core laid out at `piRoot` (its package.json,
|
|
533
|
+
* CHANGELOG.md and dist/): the installed copy, or the launcher's branded
|
|
534
|
+
* copy of a read-only install (src/bin.js).
|
|
535
|
+
*/
|
|
536
|
+
export function brandCoreDir(piRoot, { version } = {}) {
|
|
488
537
|
|
|
489
538
|
// 1. The supported rebrand hook (+ the product version: the banner and
|
|
490
539
|
// --version of the core read their own package.json).
|
|
@@ -494,7 +543,7 @@ export function applyBrandingPatches(startDir, { version } = {}) {
|
|
|
494
543
|
if (pkg.piConfig?.name !== BRAND || pkg.piConfig?.configDir !== BRAND_CONFIG_DIR || (version && pkg.version !== version)) {
|
|
495
544
|
pkg.piConfig = { ...(pkg.piConfig || {}), name: BRAND, configDir: BRAND_CONFIG_DIR };
|
|
496
545
|
if (version) pkg.version = version;
|
|
497
|
-
|
|
546
|
+
writeAtomic(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
498
547
|
packageJsonPatched = true;
|
|
499
548
|
}
|
|
500
549
|
|
|
@@ -510,7 +559,7 @@ export function applyBrandingPatches(startDir, { version } = {}) {
|
|
|
510
559
|
/* absent */
|
|
511
560
|
}
|
|
512
561
|
if (current !== ours) {
|
|
513
|
-
|
|
562
|
+
writeAtomic(target, ours);
|
|
514
563
|
changelogReplaced = true;
|
|
515
564
|
}
|
|
516
565
|
} catch {
|
|
@@ -541,7 +590,7 @@ export function applyBrandingPatches(startDir, { version } = {}) {
|
|
|
541
590
|
if (hits > 0) applied.push({ label: replacement.label, files: hits });
|
|
542
591
|
else missing.push({ label: replacement.label, reason: "target string not found (already applied or core updated)" });
|
|
543
592
|
}
|
|
544
|
-
for (const file of dirty)
|
|
593
|
+
for (const file of dirty) writeAtomic(file, contents.get(file));
|
|
545
594
|
return { piRoot, packageJsonPatched, changelogReplaced, applied, missing };
|
|
546
595
|
}
|
|
547
596
|
|
|
@@ -561,6 +610,8 @@ function main() {
|
|
|
561
610
|
// <package>/engine -> the core (what process listings and the system
|
|
562
611
|
// prompt's doc paths show; the launcher re-creates it when missing).
|
|
563
612
|
ensureEngineLink(repoRoot, report.piRoot);
|
|
613
|
+
// The launcher's first-run check (src/bin.js ensureBranded) reads this.
|
|
614
|
+
if (version) fs.writeFileSync(path.join(report.piRoot, ".omnirush-branded"), version + "\n");
|
|
564
615
|
const patched = report.packageJsonPatched ? "package.json + " : "";
|
|
565
616
|
const notes = report.changelogReplaced ? " + release notes" : "";
|
|
566
617
|
console.log(
|
package/src/bin.js
CHANGED
|
@@ -57,6 +57,7 @@ import { installManagedTools } from "./tools.js";
|
|
|
57
57
|
import { engineEnvAliases, ensureEngineLink, migrateAgentDir, projectConfigDirName, resolveAgentDir } from "./compat.js";
|
|
58
58
|
import { customSessionDir, moveLegacySubagentSessions } from "./sessions.js";
|
|
59
59
|
import { ensureRuntime, managedBunPath } from "../scripts/postinstall.js";
|
|
60
|
+
import { applyBrandingPatches, brandCoreDir } from "../scripts/brand-engine.js";
|
|
60
61
|
import {
|
|
61
62
|
clearAuth,
|
|
62
63
|
deviceAuthorize,
|
|
@@ -150,8 +151,106 @@ function piPackage() {
|
|
|
150
151
|
let ENGINE_LINK_DIR = null;
|
|
151
152
|
let ENGINE_ENTRY = null;
|
|
152
153
|
|
|
154
|
+
// Written into the core's folder once it is branded for this omnirush
|
|
155
|
+
// version. Newer npm skips install scripts by default, so the postinstall
|
|
156
|
+
// branding may never have run: the launcher then brands on first run.
|
|
157
|
+
const BRAND_STAMP = ".omnirush-branded";
|
|
158
|
+
|
|
159
|
+
function brandedStamp(dir, version) {
|
|
160
|
+
try {
|
|
161
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(dir, "package.json"), "utf8"));
|
|
162
|
+
if (pkg?.piConfig?.name === "omnirush" && fs.readFileSync(path.join(dir, BRAND_STAMP), "utf8").trim() === version) {
|
|
163
|
+
return { dir, pkg };
|
|
164
|
+
}
|
|
165
|
+
} catch {
|
|
166
|
+
/* not branded (yet) */
|
|
167
|
+
}
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* A core folder we cannot write (a root-owned global prefix installed
|
|
173
|
+
* without its scripts): run a branded copy of its runtime files instead,
|
|
174
|
+
* <state dir>/engine/<version> (package.json, release notes, README, the
|
|
175
|
+
* runtime bundle with its themes, assets and export template; docs,
|
|
176
|
+
* examples and node_modules are links to the installed ones). Made
|
|
177
|
+
* once per version, in one step (a temporary folder renamed into place).
|
|
178
|
+
*/
|
|
179
|
+
function brandedCopy(found, version) {
|
|
180
|
+
const dir = path.join(OMNI_DIR, "engine", version);
|
|
181
|
+
const ready = brandedStamp(dir, version);
|
|
182
|
+
if (ready) return ready;
|
|
183
|
+
const tmp = `${dir}.${process.pid}.tmp`;
|
|
184
|
+
try {
|
|
185
|
+
fs.rmSync(tmp, { recursive: true, force: true });
|
|
186
|
+
fs.mkdirSync(tmp, { recursive: true });
|
|
187
|
+
for (const name of ["package.json", "CHANGELOG.md", "README.md"]) {
|
|
188
|
+
if (fs.existsSync(path.join(found.dir, name))) fs.writeFileSync(path.join(tmp, name), fs.readFileSync(path.join(found.dir, name)));
|
|
189
|
+
}
|
|
190
|
+
// What the runtime bundle reads (the same set the 1.0.0 build ships).
|
|
191
|
+
// Copied by hand: the source is read-only, and its modes must not come along.
|
|
192
|
+
const copyTree = (from, to) => {
|
|
193
|
+
fs.mkdirSync(to, { recursive: true });
|
|
194
|
+
for (const entry of fs.readdirSync(from, { withFileTypes: true })) {
|
|
195
|
+
const src = path.join(from, entry.name);
|
|
196
|
+
if (entry.isDirectory()) copyTree(src, path.join(to, entry.name));
|
|
197
|
+
else if (entry.isFile() && !entry.name.endsWith(".map") && !entry.name.endsWith(".d.ts")) {
|
|
198
|
+
fs.writeFileSync(path.join(to, entry.name), fs.readFileSync(src));
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
for (const part of [["bundle"], ["modes", "interactive", "theme"], ["modes", "interactive", "assets"], ["core", "export-html"]]) {
|
|
203
|
+
const from = path.join(found.dir, "dist", ...part);
|
|
204
|
+
if (fs.existsSync(from)) copyTree(from, path.join(tmp, "dist", ...part));
|
|
205
|
+
}
|
|
206
|
+
for (const name of ["node_modules", "docs", "examples"]) {
|
|
207
|
+
const target = path.join(found.dir, name);
|
|
208
|
+
if (!fs.existsSync(target)) continue;
|
|
209
|
+
if (process.platform === "win32") fs.symlinkSync(target, path.join(tmp, name), "junction");
|
|
210
|
+
else fs.symlinkSync(target, path.join(tmp, name), "dir");
|
|
211
|
+
}
|
|
212
|
+
brandCoreDir(tmp, { version });
|
|
213
|
+
fs.writeFileSync(path.join(tmp, BRAND_STAMP), version + "\n");
|
|
214
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
215
|
+
try {
|
|
216
|
+
fs.renameSync(tmp, dir);
|
|
217
|
+
} catch (error) {
|
|
218
|
+
// A concurrent start may have finished first.
|
|
219
|
+
if (!brandedStamp(dir, version)) throw error;
|
|
220
|
+
}
|
|
221
|
+
return brandedStamp(dir, version);
|
|
222
|
+
} catch (error) {
|
|
223
|
+
if (process.env.OMNIRUSH_DEBUG === "1") process.stderr.write(`omnirush: branded copy failed: ${error?.stack || error}\n`);
|
|
224
|
+
return null;
|
|
225
|
+
} finally {
|
|
226
|
+
try {
|
|
227
|
+
fs.rmSync(tmp, { recursive: true, force: true });
|
|
228
|
+
} catch {
|
|
229
|
+
/* best effort */
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function ensureBranded(found) {
|
|
235
|
+
const version = ownVersion();
|
|
236
|
+
const done = brandedStamp(found.dir, version);
|
|
237
|
+
if (done) return done;
|
|
238
|
+
try {
|
|
239
|
+
applyBrandingPatches(path.resolve(__dirname, ".."), { version });
|
|
240
|
+
fs.writeFileSync(path.join(found.dir, BRAND_STAMP), version + "\n");
|
|
241
|
+
return { dir: found.dir, pkg: JSON.parse(fs.readFileSync(path.join(found.dir, "package.json"), "utf8")) };
|
|
242
|
+
} catch (error) {
|
|
243
|
+
// Read-only install: a branded copy in the state dir.
|
|
244
|
+
const copy = brandedCopy(found, version);
|
|
245
|
+
if (copy) return copy;
|
|
246
|
+
process.stderr.write(`omnirush: could not finish setup (${error?.code || error?.message || error}); reinstall with: npm install -g omnirush@latest\n`);
|
|
247
|
+
return found;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
153
251
|
function piEntry() {
|
|
154
|
-
|
|
252
|
+
let found = piPackage();
|
|
253
|
+
if (found?.pkg?.bin?.pi) found = ensureBranded(found);
|
|
155
254
|
if (found?.pkg?.bin?.pi) {
|
|
156
255
|
const dir = ensureEngineLink(path.resolve(__dirname, ".."), found.dir);
|
|
157
256
|
ENGINE_LINK_DIR = dir === path.resolve(found.dir) ? null : dir;
|
|
@@ -755,7 +854,8 @@ async function cmdDoctor() {
|
|
|
755
854
|
// branding and shows pi.dev "Update Available" banners (Foxtran's
|
|
756
855
|
// 0.73.1 report). Advisory WARN with the fix; exit code unaffected.
|
|
757
856
|
{
|
|
758
|
-
|
|
857
|
+
let found = piPackage();
|
|
858
|
+
if (found?.pkg?.bin?.pi) found = ensureBranded(found); // the copy that runs (a read-only install's branded copy)
|
|
759
859
|
const coreVersion = String(found?.pkg?.version ?? "");
|
|
760
860
|
const own = ownVersion();
|
|
761
861
|
if (!coreVersion) {
|
|
@@ -1049,7 +1149,6 @@ Usage:
|
|
|
1049
1149
|
omnirush logout forget the stored device tokens
|
|
1050
1150
|
omnirush whoami show the signed-in identity
|
|
1051
1151
|
omnirush doctor check auth, manager, runtime, agent config
|
|
1052
|
-
omnirush upload [--dir PATH] upload a one-shot workspace snapshot to omnirush.ai
|
|
1053
1152
|
omnirush update update omnirush (npm install -g omnirush@latest)
|
|
1054
1153
|
omnirush --version show the omnirush version
|
|
1055
1154
|
|
|
@@ -1077,7 +1176,7 @@ Options:
|
|
|
1077
1176
|
Session commands (inside the agent):
|
|
1078
1177
|
/grant today's token grant and usage
|
|
1079
1178
|
/usage detailed usage from the manager (counters + devices)
|
|
1080
|
-
/status session status (identity, model, grant
|
|
1179
|
+
/status session status (identity, model, grant)
|
|
1081
1180
|
/mcp MCP server state (/mcp reconnect [server])
|
|
1082
1181
|
/subagents model and effort of the sub-agents (/subagents model <id|same>,
|
|
1083
1182
|
/subagents effort <level|same>; saved for the next runs)
|
|
@@ -1204,6 +1303,15 @@ async function runCore(args) {
|
|
|
1204
1303
|
|
|
1205
1304
|
const [, , ...argv] = process.argv;
|
|
1206
1305
|
const cmd = argv[0];
|
|
1306
|
+
// First run after an install whose scripts were skipped: brand the core and
|
|
1307
|
+
// make the engine link now, whatever command runs (cheap once stamped).
|
|
1308
|
+
{
|
|
1309
|
+
const core = piPackage();
|
|
1310
|
+
if (core?.pkg?.bin?.pi) {
|
|
1311
|
+
const branded = ensureBranded(core);
|
|
1312
|
+
ensureEngineLink(path.resolve(__dirname, ".."), branded.dir);
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1207
1315
|
if (cmd === "-h" || cmd === "--help" || cmd === "help") printHelp();
|
|
1208
1316
|
else if (cmd === "login") await cmdLogin(argv.slice(1));
|
|
1209
1317
|
else if (cmd === "logout") await cmdLogout();
|