omnirush 0.8.1 → 0.8.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.
|
@@ -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;
|
|
@@ -2977,6 +2980,44 @@ async function writeFileAtomic(path: string, data: Buffer | string): Promise<voi
|
|
|
2977
2980
|
}
|
|
2978
2981
|
}
|
|
2979
2982
|
|
|
2983
|
+
async function withSessionLedgerLock<T>(lockPath: string, task: () => Promise<T>): Promise<T> {
|
|
2984
|
+
const started = Date.now();
|
|
2985
|
+
await mkdir(dirname(lockPath), { recursive: true, mode: 0o700 });
|
|
2986
|
+
let handle: Awaited<ReturnType<typeof open>> | null = null;
|
|
2987
|
+
while (!handle) {
|
|
2988
|
+
try {
|
|
2989
|
+
handle = await open(lockPath, "wx", 0o600);
|
|
2990
|
+
await handle.writeFile(`${process.pid}\n`);
|
|
2991
|
+
} catch (error) {
|
|
2992
|
+
await handle?.close().catch(() => undefined);
|
|
2993
|
+
handle = null;
|
|
2994
|
+
const code = typeof error === "object" && error !== null && "code" in error
|
|
2995
|
+
? (error as { code?: string }).code
|
|
2996
|
+
: undefined;
|
|
2997
|
+
if (code !== "EEXIST") throw error;
|
|
2998
|
+
try {
|
|
2999
|
+
const info = await stat(lockPath);
|
|
3000
|
+
if (Date.now() - info.mtimeMs > SESSION_LEDGER_LOCK_STALE_MS) {
|
|
3001
|
+
await rm(lockPath, { force: true });
|
|
3002
|
+
continue;
|
|
3003
|
+
}
|
|
3004
|
+
} catch {
|
|
3005
|
+
// The owner may have released the lock between EEXIST and stat.
|
|
3006
|
+
}
|
|
3007
|
+
if (Date.now() - started >= SESSION_LEDGER_LOCK_TIMEOUT_MS) {
|
|
3008
|
+
throw new Error("timed out waiting for the session ledger lock");
|
|
3009
|
+
}
|
|
3010
|
+
await new Promise<void>((resolvePromise) => setTimeout(resolvePromise, SESSION_LEDGER_LOCK_RETRY_MS));
|
|
3011
|
+
}
|
|
3012
|
+
}
|
|
3013
|
+
try {
|
|
3014
|
+
return await task();
|
|
3015
|
+
} finally {
|
|
3016
|
+
await handle.close().catch(() => undefined);
|
|
3017
|
+
await rm(lockPath, { force: true }).catch(() => undefined);
|
|
3018
|
+
}
|
|
3019
|
+
}
|
|
3020
|
+
|
|
2980
3021
|
function parseSpoolMeta(value: unknown): SpoolMeta | null {
|
|
2981
3022
|
if (!value || typeof value !== "object") return null;
|
|
2982
3023
|
const record = value as Partial<SpoolMeta>;
|
|
@@ -3105,19 +3146,27 @@ export class WorkspaceCollector {
|
|
|
3105
3146
|
this.ledger = await readSessionLedger(this.ledgerPath);
|
|
3106
3147
|
}
|
|
3107
3148
|
|
|
3108
|
-
private async saveLedger(): Promise<void> {
|
|
3149
|
+
private async saveLedger(sessionId: string): Promise<void> {
|
|
3109
3150
|
if (!this.ledgerPath) return;
|
|
3110
3151
|
await this.ledgerReady;
|
|
3111
|
-
const
|
|
3112
|
-
.
|
|
3113
|
-
|
|
3114
|
-
this.ledger.sessions = Object.fromEntries(entries);
|
|
3115
|
-
const snapshot = JSON.stringify(this.ledger);
|
|
3152
|
+
const record = this.ledger.sessions[sessionId]
|
|
3153
|
+
? JSON.parse(JSON.stringify(this.ledger.sessions[sessionId])) as SessionLedgerRecord
|
|
3154
|
+
: undefined;
|
|
3116
3155
|
this.ledgerWriteTail = this.ledgerWriteTail
|
|
3117
3156
|
.catch(() => undefined)
|
|
3118
3157
|
.then(async () => {
|
|
3119
|
-
await
|
|
3120
|
-
|
|
3158
|
+
await withSessionLedgerLock(`${this.ledgerPath!}.lock`, async () => {
|
|
3159
|
+
// Reload while holding the lock so concurrent processes merge their
|
|
3160
|
+
// session record instead of overwriting one another's progress.
|
|
3161
|
+
const merged = await readSessionLedger(this.ledgerPath);
|
|
3162
|
+
if (record) merged.sessions[sessionId] = record;
|
|
3163
|
+
const entries = Object.entries(merged.sessions)
|
|
3164
|
+
.sort(([, left], [, right]) => right.lastSeenAt.localeCompare(left.lastSeenAt))
|
|
3165
|
+
.slice(0, MAX_SESSION_LEDGER_ENTRIES);
|
|
3166
|
+
merged.sessions = Object.fromEntries(entries);
|
|
3167
|
+
this.ledger = merged;
|
|
3168
|
+
await writeFileAtomic(this.ledgerPath!, JSON.stringify(merged));
|
|
3169
|
+
});
|
|
3121
3170
|
});
|
|
3122
3171
|
await this.ledgerWriteTail;
|
|
3123
3172
|
}
|
|
@@ -3158,13 +3207,13 @@ export class WorkspaceCollector {
|
|
|
3158
3207
|
this.appendTrace(state, "session.resumed", { session_segment: state.segment, previous_segment: previous?.segment ?? null });
|
|
3159
3208
|
}
|
|
3160
3209
|
this.ledger.sessions[state.id] = this.ledgerRecord(state);
|
|
3161
|
-
await this.saveLedger();
|
|
3210
|
+
await this.saveLedger(state.id);
|
|
3162
3211
|
}
|
|
3163
3212
|
|
|
3164
3213
|
private async persistSession(state: SessionState): Promise<void> {
|
|
3165
3214
|
await this.ledgerReady;
|
|
3166
3215
|
this.ledger.sessions[state.id] = this.ledgerRecord(state);
|
|
3167
|
-
await this.saveLedger();
|
|
3216
|
+
await this.saveLedger(state.id);
|
|
3168
3217
|
}
|
|
3169
3218
|
|
|
3170
3219
|
private async recordLedgerOutcome(sessionId: string, outcome: "success" | "failure"): Promise<void> {
|
|
@@ -3188,7 +3237,7 @@ export class WorkspaceCollector {
|
|
|
3188
3237
|
record.lastFailureAt = now;
|
|
3189
3238
|
}
|
|
3190
3239
|
record.lastSeenAt = now;
|
|
3191
|
-
await this.saveLedger();
|
|
3240
|
+
await this.saveLedger(sessionId);
|
|
3192
3241
|
}
|
|
3193
3242
|
|
|
3194
3243
|
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.2",
|
|
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];
|