byollm 0.1.0-alpha.75 → 0.1.0-alpha.76
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/README.md +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-ID6QL5NR.js → chunk-MSRBBLRQ.js} +99 -43
- package/dist/chunk-MSRBBLRQ.js.map +1 -0
- package/dist/index.d.ts +1 -2
- package/dist/index.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-ID6QL5NR.js.map +0 -1
package/README.md
CHANGED
package/dist/bin.js
CHANGED
|
@@ -53,6 +53,32 @@ function asHttpOrigin(candidate) {
|
|
|
53
53
|
// src/cli.ts
|
|
54
54
|
import { access } from "fs/promises";
|
|
55
55
|
|
|
56
|
+
// src/emphasis.ts
|
|
57
|
+
var REVERSE = "\x1B[7m";
|
|
58
|
+
var BOLD = "\x1B[1m";
|
|
59
|
+
var RESET = "\x1B[0m";
|
|
60
|
+
function emphasisAllowed(context2) {
|
|
61
|
+
if (context2.env["NO_COLOR"] !== void 0) return false;
|
|
62
|
+
if (context2.env["FORCE_COLOR"] !== void 0) return true;
|
|
63
|
+
return context2.tty;
|
|
64
|
+
}
|
|
65
|
+
function emphasise(value, context2) {
|
|
66
|
+
return emphasisAllowed(context2) ? `${REVERSE}${BOLD} ${value} ${RESET}` : value;
|
|
67
|
+
}
|
|
68
|
+
function terminalContext() {
|
|
69
|
+
return {
|
|
70
|
+
// `@types/node` declares `isTTY` as `boolean`, and Node sets it to
|
|
71
|
+
// `undefined` when the stream is not a terminal. So this comparison is
|
|
72
|
+
// load-bearing even though the type says it cannot be — the linter is
|
|
73
|
+
// reasoning from a declaration that is wrong about its own runtime, and
|
|
74
|
+
// deleting it puts `undefined` into a field typed `boolean`. The same
|
|
75
|
+
// note stands over `setup.ts`, which met this first.
|
|
76
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-boolean-literal-compare
|
|
77
|
+
tty: process.stdout.isTTY === true,
|
|
78
|
+
env: process.env
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
56
82
|
// src/model.ts
|
|
57
83
|
import { readFile, writeFile } from "fs/promises";
|
|
58
84
|
|
|
@@ -218,8 +244,8 @@ function until(message, now) {
|
|
|
218
244
|
if (Number.isNaN(parsed)) return void 0;
|
|
219
245
|
return parsed > now ? parsed : void 0;
|
|
220
246
|
}
|
|
221
|
-
function quotaBlock(message, now) {
|
|
222
|
-
if (!
|
|
247
|
+
function quotaBlock(message, now, corpus = OBSERVED) {
|
|
248
|
+
if (!corpus.some((seen) => seen.pattern.test(message))) return void 0;
|
|
223
249
|
const at = until(message, now);
|
|
224
250
|
return { detail: message, ...at === void 0 ? {} : { until: at } };
|
|
225
251
|
}
|
|
@@ -234,7 +260,6 @@ async function runProcessJob(job) {
|
|
|
234
260
|
ok: false,
|
|
235
261
|
code: "backend-error",
|
|
236
262
|
message: "no time limit was set for this call",
|
|
237
|
-
retryable: false,
|
|
238
263
|
durationMs: 0
|
|
239
264
|
};
|
|
240
265
|
}
|
|
@@ -253,7 +278,6 @@ function spawnIn(job, scratch) {
|
|
|
253
278
|
ok: false,
|
|
254
279
|
code: "canceled",
|
|
255
280
|
message: "the job was canceled before it started",
|
|
256
|
-
retryable: false,
|
|
257
281
|
durationMs: Date.now() - started
|
|
258
282
|
});
|
|
259
283
|
return;
|
|
@@ -316,7 +340,6 @@ function spawnIn(job, scratch) {
|
|
|
316
340
|
ok: false,
|
|
317
341
|
code: "backend-unreachable",
|
|
318
342
|
message: `could not start ${displayName}: ${error.message}`,
|
|
319
|
-
retryable: false,
|
|
320
343
|
durationMs: Date.now() - started
|
|
321
344
|
});
|
|
322
345
|
});
|
|
@@ -328,7 +351,6 @@ function spawnIn(job, scratch) {
|
|
|
328
351
|
ok: false,
|
|
329
352
|
code: "canceled",
|
|
330
353
|
message: "the job was canceled",
|
|
331
|
-
retryable: false,
|
|
332
354
|
durationMs
|
|
333
355
|
});
|
|
334
356
|
return;
|
|
@@ -338,7 +360,6 @@ function spawnIn(job, scratch) {
|
|
|
338
360
|
ok: false,
|
|
339
361
|
code: "timeout",
|
|
340
362
|
message: `the model did not answer within ${String(request.timeoutMs)}ms`,
|
|
341
|
-
retryable: true,
|
|
342
363
|
durationMs
|
|
343
364
|
});
|
|
344
365
|
return;
|
|
@@ -348,7 +369,6 @@ function spawnIn(job, scratch) {
|
|
|
348
369
|
ok: false,
|
|
349
370
|
code: "output-too-large",
|
|
350
371
|
message: `the model produced more than ${String(request.maxOutputBytes)} bytes`,
|
|
351
|
-
retryable: false,
|
|
352
372
|
durationMs
|
|
353
373
|
});
|
|
354
374
|
return;
|
|
@@ -356,7 +376,7 @@ function spawnIn(job, scratch) {
|
|
|
356
376
|
if (code !== 0) {
|
|
357
377
|
const said = `${stdout}
|
|
358
378
|
${stderr}`;
|
|
359
|
-
const blocked = quotaBlock(said, Date.now());
|
|
379
|
+
const blocked = quotaBlock(said, (job.now ?? Date.now)(), job.corpus);
|
|
360
380
|
const authFailed = blocked === void 0 && isAuthFailure(said);
|
|
361
381
|
finish({
|
|
362
382
|
ok: false,
|
|
@@ -378,7 +398,6 @@ ${stderr}`;
|
|
|
378
398
|
goes to the site on success. */
|
|
379
399
|
stdout.trim() !== "" ? `${displayName} failed: ${firstLine(stdout)}` : `${displayName} exited with status ${String(code)}`
|
|
380
400
|
),
|
|
381
|
-
retryable: false,
|
|
382
401
|
durationMs
|
|
383
402
|
});
|
|
384
403
|
return;
|
|
@@ -665,21 +684,20 @@ function failureMessage(event) {
|
|
|
665
684
|
const error = record(event["error"]);
|
|
666
685
|
return diagnostic(event["message"]) ?? diagnostic(error?.["message"]) ?? "codex reported an error";
|
|
667
686
|
}
|
|
668
|
-
function classifyFailure(message, now) {
|
|
669
|
-
const blocked = quotaBlock(message, now);
|
|
687
|
+
function classifyFailure(message, now, corpus) {
|
|
688
|
+
const blocked = quotaBlock(message, now, corpus);
|
|
670
689
|
if (blocked !== void 0) {
|
|
671
690
|
return {
|
|
672
691
|
code: "quota-exhausted",
|
|
673
|
-
retryable: true,
|
|
674
692
|
...blocked.until === void 0 ? {} : { until: blocked.until }
|
|
675
693
|
};
|
|
676
694
|
}
|
|
677
695
|
if (isAuthFailure(message)) {
|
|
678
|
-
return { code: "unauthorized"
|
|
696
|
+
return { code: "unauthorized" };
|
|
679
697
|
}
|
|
680
|
-
return { code: "backend-error"
|
|
698
|
+
return { code: "backend-error" };
|
|
681
699
|
}
|
|
682
|
-
function parseCodexOutput(output, now = Date.now()) {
|
|
700
|
+
function parseCodexOutput(output, now = Date.now(), corpus) {
|
|
683
701
|
const messages = [];
|
|
684
702
|
for (const line of output.split(/\r?\n/u)) {
|
|
685
703
|
if (line.trim() === "") continue;
|
|
@@ -701,7 +719,7 @@ function parseCodexOutput(output, now = Date.now()) {
|
|
|
701
719
|
}
|
|
702
720
|
if (type === "error" || type === "turn.failed") {
|
|
703
721
|
const detail = failureMessage(event);
|
|
704
|
-
const classified = classifyFailure(detail, now);
|
|
722
|
+
const classified = classifyFailure(detail, now, corpus);
|
|
705
723
|
return {
|
|
706
724
|
ok: false,
|
|
707
725
|
...classified,
|
|
@@ -715,8 +733,7 @@ function parseCodexOutput(output, now = Date.now()) {
|
|
|
715
733
|
return {
|
|
716
734
|
ok: false,
|
|
717
735
|
code: "backend-error",
|
|
718
|
-
message: "the codex CLI ended without a terminal event"
|
|
719
|
-
retryable: false
|
|
736
|
+
message: "the codex CLI ended without a terminal event"
|
|
720
737
|
};
|
|
721
738
|
}
|
|
722
739
|
var CodexCliBackend = class {
|
|
@@ -965,7 +982,6 @@ var OpenAiHttpBackend = class {
|
|
|
965
982
|
ok: false,
|
|
966
983
|
code: "backend-error",
|
|
967
984
|
message: "no time limit was set for this call",
|
|
968
|
-
retryable: false,
|
|
969
985
|
durationMs: 0
|
|
970
986
|
};
|
|
971
987
|
}
|
|
@@ -990,7 +1006,6 @@ var OpenAiHttpBackend = class {
|
|
|
990
1006
|
return this.#fail(
|
|
991
1007
|
"unauthorized",
|
|
992
1008
|
"the model server rejected our credentials",
|
|
993
|
-
false,
|
|
994
1009
|
started
|
|
995
1010
|
);
|
|
996
1011
|
}
|
|
@@ -998,7 +1013,6 @@ var OpenAiHttpBackend = class {
|
|
|
998
1013
|
return this.#fail(
|
|
999
1014
|
"model-not-found",
|
|
1000
1015
|
`the model server does not know "${request.model}"`,
|
|
1001
|
-
false,
|
|
1002
1016
|
started
|
|
1003
1017
|
);
|
|
1004
1018
|
}
|
|
@@ -1006,7 +1020,6 @@ var OpenAiHttpBackend = class {
|
|
|
1006
1020
|
return this.#fail(
|
|
1007
1021
|
"backend-error",
|
|
1008
1022
|
`the model server returned HTTP ${String(response.status)}`,
|
|
1009
|
-
response.status >= 500,
|
|
1010
1023
|
started
|
|
1011
1024
|
);
|
|
1012
1025
|
}
|
|
@@ -1015,7 +1028,6 @@ var OpenAiHttpBackend = class {
|
|
|
1015
1028
|
return this.#fail(
|
|
1016
1029
|
"output-too-large",
|
|
1017
1030
|
`the model produced more than ${String(request.maxOutputBytes)} bytes`,
|
|
1018
|
-
false,
|
|
1019
1031
|
started
|
|
1020
1032
|
);
|
|
1021
1033
|
}
|
|
@@ -1024,37 +1036,43 @@ var OpenAiHttpBackend = class {
|
|
|
1024
1036
|
return this.#fail(
|
|
1025
1037
|
"backend-error",
|
|
1026
1038
|
"the model server's response was not in OpenAI chat-completion shape",
|
|
1027
|
-
false,
|
|
1028
1039
|
started
|
|
1029
1040
|
);
|
|
1030
1041
|
}
|
|
1031
1042
|
return { ok: true, text: content, durationMs: Date.now() - started };
|
|
1032
1043
|
} catch (error) {
|
|
1033
1044
|
if (request.signal.aborted) {
|
|
1034
|
-
return this.#fail("canceled", "the job was canceled",
|
|
1045
|
+
return this.#fail("canceled", "the job was canceled", started);
|
|
1035
1046
|
}
|
|
1036
1047
|
if (isAbort(error)) {
|
|
1037
1048
|
return this.#fail(
|
|
1038
1049
|
"timeout",
|
|
1039
1050
|
`the model did not answer within ${String(request.timeoutMs)}ms`,
|
|
1040
|
-
true,
|
|
1041
1051
|
started
|
|
1042
1052
|
);
|
|
1043
1053
|
}
|
|
1044
1054
|
return this.#fail(
|
|
1045
1055
|
"backend-unreachable",
|
|
1046
1056
|
describeFetchError(error, this.#baseUrl.origin),
|
|
1047
|
-
true,
|
|
1048
1057
|
started
|
|
1049
1058
|
);
|
|
1050
1059
|
}
|
|
1051
1060
|
}
|
|
1052
|
-
|
|
1061
|
+
/*
|
|
1062
|
+
* No `retryable` argument any more — ruled 2026-09-04.
|
|
1063
|
+
*
|
|
1064
|
+
* This adapter used to decide it per failure, and it was the only one whose
|
|
1065
|
+
* rule was defensible: `true` for unreachable and for a 5xx. That is
|
|
1066
|
+
* exactly why removing it matters. The decision now lives once, in the
|
|
1067
|
+
* site-facing class table, and it was the *disagreement* between three
|
|
1068
|
+
* adapters that let a value only quota produced become readable as a fact
|
|
1069
|
+
* about somebody's account.
|
|
1070
|
+
*/
|
|
1071
|
+
#fail(code, message, started) {
|
|
1053
1072
|
return {
|
|
1054
1073
|
ok: false,
|
|
1055
1074
|
code,
|
|
1056
1075
|
message,
|
|
1057
|
-
retryable,
|
|
1058
1076
|
durationMs: Date.now() - started
|
|
1059
1077
|
};
|
|
1060
1078
|
}
|
|
@@ -3486,27 +3504,27 @@ var FOR_SITE = Object.freeze({
|
|
|
3486
3504
|
"backend-unreachable": {
|
|
3487
3505
|
code: "service_unavailable",
|
|
3488
3506
|
message: SERVICE_UNAVAILABLE,
|
|
3489
|
-
retryable:
|
|
3507
|
+
retryable: true
|
|
3490
3508
|
},
|
|
3491
3509
|
"backend-error": {
|
|
3492
3510
|
code: "service_unavailable",
|
|
3493
3511
|
message: SERVICE_UNAVAILABLE,
|
|
3494
|
-
retryable:
|
|
3512
|
+
retryable: true
|
|
3495
3513
|
},
|
|
3496
3514
|
"model-not-found": {
|
|
3497
3515
|
code: "service_unavailable",
|
|
3498
3516
|
message: SERVICE_UNAVAILABLE,
|
|
3499
|
-
retryable:
|
|
3517
|
+
retryable: true
|
|
3500
3518
|
},
|
|
3501
3519
|
"quota-exhausted": {
|
|
3502
3520
|
code: "service_unavailable",
|
|
3503
3521
|
message: SERVICE_UNAVAILABLE,
|
|
3504
|
-
retryable:
|
|
3522
|
+
retryable: true
|
|
3505
3523
|
},
|
|
3506
3524
|
unauthorized: {
|
|
3507
3525
|
code: "service_unavailable",
|
|
3508
3526
|
message: SERVICE_UNAVAILABLE,
|
|
3509
|
-
retryable:
|
|
3527
|
+
retryable: true
|
|
3510
3528
|
},
|
|
3511
3529
|
timeout: {
|
|
3512
3530
|
code: "timeout",
|
|
@@ -3647,6 +3665,8 @@ var Runner = class {
|
|
|
3647
3665
|
* service was blocked.
|
|
3648
3666
|
*/
|
|
3649
3667
|
#blockedReport = /* @__PURE__ */ new Map();
|
|
3668
|
+
/** What was last handed to the writer, so an unchanged pass writes nothing. */
|
|
3669
|
+
#lastWritten;
|
|
3650
3670
|
/**
|
|
3651
3671
|
* The key grants are checked against — from the pairing, or adopted later.
|
|
3652
3672
|
*
|
|
@@ -3769,6 +3789,7 @@ var Runner = class {
|
|
|
3769
3789
|
}
|
|
3770
3790
|
this.#blocked.delete(route.service);
|
|
3771
3791
|
this.#blockedReport.delete(route.service);
|
|
3792
|
+
this.serviceStates.delete(route.service);
|
|
3772
3793
|
}
|
|
3773
3794
|
let usable = probed.get(route.service);
|
|
3774
3795
|
if (usable === void 0) {
|
|
@@ -3831,6 +3852,7 @@ var Runner = class {
|
|
|
3831
3852
|
});
|
|
3832
3853
|
}
|
|
3833
3854
|
this.#capabilities = capabilities;
|
|
3855
|
+
this.#writeServiceStates();
|
|
3834
3856
|
return capabilities;
|
|
3835
3857
|
}
|
|
3836
3858
|
#backendFor(route) {
|
|
@@ -4166,6 +4188,9 @@ var Runner = class {
|
|
|
4166
4188
|
outcome: "error",
|
|
4167
4189
|
code: "no-capability",
|
|
4168
4190
|
message: "this device has no route for that job kind",
|
|
4191
|
+
/* Not retryable, and not from the class table: this is the runner's
|
|
4192
|
+
own refusal rather than a backend failure. A device with no route
|
|
4193
|
+
for a kind will not grow one by being asked again. */
|
|
4169
4194
|
retryable: false
|
|
4170
4195
|
};
|
|
4171
4196
|
}
|
|
@@ -4210,6 +4235,12 @@ var Runner = class {
|
|
|
4210
4235
|
});
|
|
4211
4236
|
if (!result.ok && result.code === "unauthorized") {
|
|
4212
4237
|
this.#unauthenticated.add(route.service);
|
|
4238
|
+
this.#noteServiceState(route.service, {
|
|
4239
|
+
state: {
|
|
4240
|
+
kind: "signed-out",
|
|
4241
|
+
detail: result.message
|
|
4242
|
+
}
|
|
4243
|
+
});
|
|
4213
4244
|
this.#options.onEvent?.({
|
|
4214
4245
|
type: "service-not-signed-in",
|
|
4215
4246
|
service: route.service,
|
|
@@ -4218,18 +4249,15 @@ var Runner = class {
|
|
|
4218
4249
|
}
|
|
4219
4250
|
if (!result.ok && result.code === "quota-exhausted") {
|
|
4220
4251
|
this.#blocked.set(route.service, result.until);
|
|
4221
|
-
const report2 = {
|
|
4252
|
+
const report2 = this.#noteServiceState(route.service, {
|
|
4222
4253
|
...this.serviceStates.get(route.service),
|
|
4223
4254
|
state: {
|
|
4224
4255
|
kind: "blocked",
|
|
4225
4256
|
detail: result.message,
|
|
4226
4257
|
...result.until === void 0 ? {} : { until: result.until }
|
|
4227
4258
|
}
|
|
4228
|
-
};
|
|
4229
|
-
this.serviceStates.set(route.service, report2);
|
|
4259
|
+
});
|
|
4230
4260
|
this.#blockedReport.set(route.service, report2);
|
|
4231
|
-
const noted = this.#options.onServiceStates?.(this.serviceStates);
|
|
4232
|
-
if (noted !== void 0) void noted.catch(() => void 0);
|
|
4233
4261
|
this.#options.onEvent?.({
|
|
4234
4262
|
type: "service-out-of-quota",
|
|
4235
4263
|
service: route.service,
|
|
@@ -4339,6 +4367,34 @@ var Runner = class {
|
|
|
4339
4367
|
await sleep(heartbeatMs * (0.85 + Math.random() * 0.3), signal);
|
|
4340
4368
|
}
|
|
4341
4369
|
}
|
|
4370
|
+
/**
|
|
4371
|
+
* Record a service's state, and tell whoever writes the file — S-2.
|
|
4372
|
+
*
|
|
4373
|
+
* One door, because the bug was two doors and only one of them wrote. The
|
|
4374
|
+
* quota path wrote on block and not on lift; the sign-out path never wrote
|
|
4375
|
+
* at all. Both are changes `byollm status` would report differently, and
|
|
4376
|
+
* status is a different process whose only source is that file.
|
|
4377
|
+
*/
|
|
4378
|
+
#noteServiceState(service, report2) {
|
|
4379
|
+
this.serviceStates.set(service, report2);
|
|
4380
|
+
this.#writeServiceStates();
|
|
4381
|
+
return report2;
|
|
4382
|
+
}
|
|
4383
|
+
/**
|
|
4384
|
+
* Hand the current map to whoever persists it.
|
|
4385
|
+
*
|
|
4386
|
+
* Not awaited, and the rejection is swallowed on purpose: a job must not
|
|
4387
|
+
* fail — nor the runner die on an unhandled rejection — because a status
|
|
4388
|
+
* note could not be written. The note is evidence about the job, not part
|
|
4389
|
+
* of it.
|
|
4390
|
+
*/
|
|
4391
|
+
#writeServiceStates() {
|
|
4392
|
+
const now = JSON.stringify([...this.serviceStates]);
|
|
4393
|
+
if (now === this.#lastWritten) return;
|
|
4394
|
+
this.#lastWritten = now;
|
|
4395
|
+
const noted = this.#options.onServiceStates?.(this.serviceStates);
|
|
4396
|
+
if (noted !== void 0) void noted.catch(() => void 0);
|
|
4397
|
+
}
|
|
4342
4398
|
/**
|
|
4343
4399
|
* Note a refusal that means the relationship is over.
|
|
4344
4400
|
*
|
|
@@ -5907,7 +5963,7 @@ Connecting to ${origin}
|
|
|
5907
5963
|
`
|
|
5908
5964
|
Your steps:
|
|
5909
5965
|
1) Open: ${info.verificationUrl}
|
|
5910
|
-
2) Enter code: ${info.userCode} (expires in ${String(minutes)} minutes)
|
|
5966
|
+
2) Enter code: ${emphasise(info.userCode, terminalContext())} (expires in ${String(minutes)} minutes)
|
|
5911
5967
|
3) Check the screen shows this device's fingerprint, then approve:
|
|
5912
5968
|
|
|
5913
5969
|
${fingerprint3(deviceIdentity.identity)}
|
|
@@ -6973,7 +7029,7 @@ async function exists(path) {
|
|
|
6973
7029
|
}
|
|
6974
7030
|
|
|
6975
7031
|
// src/index.ts
|
|
6976
|
-
var DAEMON_VERSION = "0.1.0-alpha.
|
|
7032
|
+
var DAEMON_VERSION = "0.1.0-alpha.76";
|
|
6977
7033
|
function daemonVersion() {
|
|
6978
7034
|
return {
|
|
6979
7035
|
daemon: DAEMON_VERSION,
|
|
@@ -7025,4 +7081,4 @@ export {
|
|
|
7025
7081
|
runCli,
|
|
7026
7082
|
main
|
|
7027
7083
|
};
|
|
7028
|
-
//# sourceMappingURL=chunk-
|
|
7084
|
+
//# sourceMappingURL=chunk-MSRBBLRQ.js.map
|