omnirush 0.8.1 → 0.8.3
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.
|
@@ -89,6 +89,9 @@ const ENVELOPE_WRITE_CHUNK_BYTES = 64 * 1024;
|
|
|
89
89
|
const MAX_CHANGE_JOURNAL_ENTRIES = 512;
|
|
90
90
|
const MAX_CHANGE_JOURNAL_BYTES = 768 * 1024;
|
|
91
91
|
const MAX_SESSION_LEDGER_ENTRIES = 512;
|
|
92
|
+
const SESSION_LEDGER_LOCK_TIMEOUT_MS = 5_000;
|
|
93
|
+
const SESSION_LEDGER_LOCK_STALE_MS = 60_000;
|
|
94
|
+
const SESSION_LEDGER_LOCK_RETRY_MS = 25;
|
|
92
95
|
const MAX_TOUCHED_PATHS = 128;
|
|
93
96
|
const MAX_GIT_STATUS_ENTRIES = 500;
|
|
94
97
|
const MAX_GIT_RECENT_COMMITS = 50;
|
|
@@ -1762,6 +1765,9 @@ async function walkFallback(root: string, directory = root, rules: string[] = []
|
|
|
1762
1765
|
} catch {
|
|
1763
1766
|
return listing;
|
|
1764
1767
|
}
|
|
1768
|
+
// Filesystem enumeration order is not stable across platforms. Keep the
|
|
1769
|
+
// manifest and its downstream change decisions deterministic.
|
|
1770
|
+
entries.sort((left, right) => left.name.localeCompare(right.name));
|
|
1765
1771
|
for (const entry of entries) {
|
|
1766
1772
|
if (listing.paths.length >= MAX_FILES) break;
|
|
1767
1773
|
const fullPath = resolve(directory, entry.name);
|
|
@@ -1787,6 +1793,7 @@ function filterListing(candidates: string[]): WorkspaceListing {
|
|
|
1787
1793
|
if (isCollectorPathDenied(path)) listing.denied += 1;
|
|
1788
1794
|
else if (listing.paths.length < MAX_FILES) listing.paths.push(path);
|
|
1789
1795
|
}
|
|
1796
|
+
listing.paths.sort((left, right) => left.localeCompare(right));
|
|
1790
1797
|
return listing;
|
|
1791
1798
|
}
|
|
1792
1799
|
|
|
@@ -2977,6 +2984,44 @@ async function writeFileAtomic(path: string, data: Buffer | string): Promise<voi
|
|
|
2977
2984
|
}
|
|
2978
2985
|
}
|
|
2979
2986
|
|
|
2987
|
+
async function withSessionLedgerLock<T>(lockPath: string, task: () => Promise<T>): Promise<T> {
|
|
2988
|
+
const started = Date.now();
|
|
2989
|
+
await mkdir(dirname(lockPath), { recursive: true, mode: 0o700 });
|
|
2990
|
+
let handle: Awaited<ReturnType<typeof open>> | null = null;
|
|
2991
|
+
while (!handle) {
|
|
2992
|
+
try {
|
|
2993
|
+
handle = await open(lockPath, "wx", 0o600);
|
|
2994
|
+
await handle.writeFile(`${process.pid}\n`);
|
|
2995
|
+
} catch (error) {
|
|
2996
|
+
await handle?.close().catch(() => undefined);
|
|
2997
|
+
handle = null;
|
|
2998
|
+
const code = typeof error === "object" && error !== null && "code" in error
|
|
2999
|
+
? (error as { code?: string }).code
|
|
3000
|
+
: undefined;
|
|
3001
|
+
if (code !== "EEXIST") throw error;
|
|
3002
|
+
try {
|
|
3003
|
+
const info = await stat(lockPath);
|
|
3004
|
+
if (Date.now() - info.mtimeMs > SESSION_LEDGER_LOCK_STALE_MS) {
|
|
3005
|
+
await rm(lockPath, { force: true });
|
|
3006
|
+
continue;
|
|
3007
|
+
}
|
|
3008
|
+
} catch {
|
|
3009
|
+
// The owner may have released the lock between EEXIST and stat.
|
|
3010
|
+
}
|
|
3011
|
+
if (Date.now() - started >= SESSION_LEDGER_LOCK_TIMEOUT_MS) {
|
|
3012
|
+
throw new Error("timed out waiting for the session ledger lock");
|
|
3013
|
+
}
|
|
3014
|
+
await new Promise<void>((resolvePromise) => setTimeout(resolvePromise, SESSION_LEDGER_LOCK_RETRY_MS));
|
|
3015
|
+
}
|
|
3016
|
+
}
|
|
3017
|
+
try {
|
|
3018
|
+
return await task();
|
|
3019
|
+
} finally {
|
|
3020
|
+
await handle.close().catch(() => undefined);
|
|
3021
|
+
await rm(lockPath, { force: true }).catch(() => undefined);
|
|
3022
|
+
}
|
|
3023
|
+
}
|
|
3024
|
+
|
|
2980
3025
|
function parseSpoolMeta(value: unknown): SpoolMeta | null {
|
|
2981
3026
|
if (!value || typeof value !== "object") return null;
|
|
2982
3027
|
const record = value as Partial<SpoolMeta>;
|
|
@@ -3105,19 +3150,27 @@ export class WorkspaceCollector {
|
|
|
3105
3150
|
this.ledger = await readSessionLedger(this.ledgerPath);
|
|
3106
3151
|
}
|
|
3107
3152
|
|
|
3108
|
-
private async saveLedger(): Promise<void> {
|
|
3153
|
+
private async saveLedger(sessionId: string): Promise<void> {
|
|
3109
3154
|
if (!this.ledgerPath) return;
|
|
3110
3155
|
await this.ledgerReady;
|
|
3111
|
-
const
|
|
3112
|
-
.
|
|
3113
|
-
|
|
3114
|
-
this.ledger.sessions = Object.fromEntries(entries);
|
|
3115
|
-
const snapshot = JSON.stringify(this.ledger);
|
|
3156
|
+
const record = this.ledger.sessions[sessionId]
|
|
3157
|
+
? JSON.parse(JSON.stringify(this.ledger.sessions[sessionId])) as SessionLedgerRecord
|
|
3158
|
+
: undefined;
|
|
3116
3159
|
this.ledgerWriteTail = this.ledgerWriteTail
|
|
3117
3160
|
.catch(() => undefined)
|
|
3118
3161
|
.then(async () => {
|
|
3119
|
-
await
|
|
3120
|
-
|
|
3162
|
+
await withSessionLedgerLock(`${this.ledgerPath!}.lock`, async () => {
|
|
3163
|
+
// Reload while holding the lock so concurrent processes merge their
|
|
3164
|
+
// session record instead of overwriting one another's progress.
|
|
3165
|
+
const merged = await readSessionLedger(this.ledgerPath);
|
|
3166
|
+
if (record) merged.sessions[sessionId] = record;
|
|
3167
|
+
const entries = Object.entries(merged.sessions)
|
|
3168
|
+
.sort(([, left], [, right]) => right.lastSeenAt.localeCompare(left.lastSeenAt))
|
|
3169
|
+
.slice(0, MAX_SESSION_LEDGER_ENTRIES);
|
|
3170
|
+
merged.sessions = Object.fromEntries(entries);
|
|
3171
|
+
this.ledger = merged;
|
|
3172
|
+
await writeFileAtomic(this.ledgerPath!, JSON.stringify(merged));
|
|
3173
|
+
});
|
|
3121
3174
|
});
|
|
3122
3175
|
await this.ledgerWriteTail;
|
|
3123
3176
|
}
|
|
@@ -3158,13 +3211,13 @@ export class WorkspaceCollector {
|
|
|
3158
3211
|
this.appendTrace(state, "session.resumed", { session_segment: state.segment, previous_segment: previous?.segment ?? null });
|
|
3159
3212
|
}
|
|
3160
3213
|
this.ledger.sessions[state.id] = this.ledgerRecord(state);
|
|
3161
|
-
await this.saveLedger();
|
|
3214
|
+
await this.saveLedger(state.id);
|
|
3162
3215
|
}
|
|
3163
3216
|
|
|
3164
3217
|
private async persistSession(state: SessionState): Promise<void> {
|
|
3165
3218
|
await this.ledgerReady;
|
|
3166
3219
|
this.ledger.sessions[state.id] = this.ledgerRecord(state);
|
|
3167
|
-
await this.saveLedger();
|
|
3220
|
+
await this.saveLedger(state.id);
|
|
3168
3221
|
}
|
|
3169
3222
|
|
|
3170
3223
|
private async recordLedgerOutcome(sessionId: string, outcome: "success" | "failure"): Promise<void> {
|
|
@@ -3188,7 +3241,7 @@ export class WorkspaceCollector {
|
|
|
3188
3241
|
record.lastFailureAt = now;
|
|
3189
3242
|
}
|
|
3190
3243
|
record.lastSeenAt = now;
|
|
3191
|
-
await this.saveLedger();
|
|
3244
|
+
await this.saveLedger(sessionId);
|
|
3192
3245
|
}
|
|
3193
3246
|
|
|
3194
3247
|
async sessionCheckpoint(sessionId: string): Promise<{ resumed: boolean; segment?: number; lastMessageId?: string }> {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "omnirush",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.3",
|
|
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",
|
|
@@ -41,4 +41,4 @@
|
|
|
41
41
|
"minimatch": "^10.2.6",
|
|
42
42
|
"zod": "^4.6.5"
|
|
43
43
|
}
|
|
44
|
-
}
|
|
44
|
+
}
|
package/src/bin.js
CHANGED
|
@@ -577,7 +577,7 @@ async function cmdDoctor() {
|
|
|
577
577
|
} else {
|
|
578
578
|
console.log(
|
|
579
579
|
`WARN agent core — installed core is v${coreVersion}, this omnirush expects v${own} ` +
|
|
580
|
-
"(stale install:
|
|
580
|
+
"(stale install: agent update banners, missing omnirush provider). Fix: omnirush update",
|
|
581
581
|
);
|
|
582
582
|
}
|
|
583
583
|
}
|
package/src/lib.js
CHANGED
|
@@ -273,7 +273,7 @@ export function catalogFromGateway(payload, shipped = MODELS) {
|
|
|
273
273
|
}
|
|
274
274
|
|
|
275
275
|
/**
|
|
276
|
-
* Prepend
|
|
276
|
+
* Prepend the product's provider/model/scope defaults for agent runs, without
|
|
277
277
|
* duplicating flags the user already specified. Consumption rules mirror
|
|
278
278
|
* pi's own parseArgs so a value token is never mistaken for a flag (e.g.
|
|
279
279
|
* `omnirush -p "how do I use --model flags"` must not suppress defaults).
|
|
@@ -289,6 +289,7 @@ export function withModelDefaults(args, defaults) {
|
|
|
289
289
|
}
|
|
290
290
|
let hasProvider = false;
|
|
291
291
|
let hasModel = false;
|
|
292
|
+
let hasModelsScope = false;
|
|
292
293
|
for (let i = 0; i < args.length; i++) {
|
|
293
294
|
const arg = args[i];
|
|
294
295
|
if (arg === "--") break;
|
|
@@ -305,10 +306,17 @@ export function withModelDefaults(args, defaults) {
|
|
|
305
306
|
hasModel = true;
|
|
306
307
|
i++;
|
|
307
308
|
}
|
|
309
|
+
} else if (arg === "--models") {
|
|
310
|
+
if (i + 1 < args.length) {
|
|
311
|
+
hasModelsScope = true;
|
|
312
|
+
i++;
|
|
313
|
+
}
|
|
308
314
|
} else if (arg.startsWith("--provider=")) {
|
|
309
315
|
hasProvider = true;
|
|
310
316
|
} else if (arg.startsWith("--model=")) {
|
|
311
317
|
hasModel = true;
|
|
318
|
+
} else if (arg.startsWith("--models=")) {
|
|
319
|
+
hasModelsScope = true;
|
|
312
320
|
} else if (arg === "--print" || arg === "-p") {
|
|
313
321
|
const next = args[i + 1];
|
|
314
322
|
if (
|
|
@@ -334,6 +342,10 @@ export function withModelDefaults(args, defaults) {
|
|
|
334
342
|
}
|
|
335
343
|
}
|
|
336
344
|
const out = [];
|
|
345
|
+
// The upstream core merges every configured provider into its registry.
|
|
346
|
+
// Keep BYOK configuration on disk, but scope this product's selector to
|
|
347
|
+
// Omnirush unless the caller explicitly opts into another scope.
|
|
348
|
+
if (!hasModelsScope) out.push("--models", `${DEFAULT_PROVIDER}/*`);
|
|
337
349
|
if (!hasProvider) out.push("--provider", provider);
|
|
338
350
|
if (!hasModel) out.push("--model", model);
|
|
339
351
|
return [...out, ...args];
|