@bridge4dev/runner 0.49.0 → 0.51.0
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/dist/agent-versions.d.ts +33 -2
- package/dist/agent-versions.js +29 -5
- package/dist/index.js +6 -0
- package/dist/supervisor.d.ts +11 -1
- package/dist/supervisor.js +51 -2
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/agent-versions.d.ts
CHANGED
|
@@ -58,8 +58,39 @@ export interface ProbeResult {
|
|
|
58
58
|
stdout: string;
|
|
59
59
|
stderr: string;
|
|
60
60
|
}
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
export interface ProbeOptions {
|
|
62
|
+
/**
|
|
63
|
+
* Keep the output of a command that exited non-zero.
|
|
64
|
+
*
|
|
65
|
+
* **This is the bug the flag exists for, and it is worth stating in full.**
|
|
66
|
+
* `codex doctor --json` exits 1 whenever ANY of its checks fails — and three
|
|
67
|
+
* of them are network checks (`network.provider_reachability`,
|
|
68
|
+
* `network.websocket_reachability`, `updates.status`) that have nothing to
|
|
69
|
+
* do with the question we are asking. Measured on a live machine
|
|
70
|
+
* (codex-cli 0.153.4): with the network reachable, exit 0; with it blocked,
|
|
71
|
+
* **exit 1 while `runtime.provenance` still reports `status: ok` and
|
|
72
|
+
* `install method: npm (…)` on stdout**.
|
|
73
|
+
*
|
|
74
|
+
* So a passing hiccup on somebody's dev server used to throw away an answer
|
|
75
|
+
* we already held, the machine reported «install method unknown», and the
|
|
76
|
+
* «Update» button disappeared from that agent's row for the hour the
|
|
77
|
+
* measurement is cached. It came back on its own later, which is why it read
|
|
78
|
+
* as a permanent property of three machines rather than as a fault.
|
|
79
|
+
*
|
|
80
|
+
* A verdict about the machine's health is not a verdict about whether the
|
|
81
|
+
* command told us what we asked. Timeouts and «no such binary» are still
|
|
82
|
+
* failures — those really are «no answer».
|
|
83
|
+
*/
|
|
84
|
+
tolerateExitCode?: boolean;
|
|
85
|
+
}
|
|
86
|
+
/** Run one probe. Rejects on a timeout, a missing binary, or (by default) a non-zero exit. */
|
|
87
|
+
export type ProbeRunner = (file: string, argv: readonly string[], timeoutMs: number, options?: ProbeOptions) => Promise<ProbeResult>;
|
|
88
|
+
/**
|
|
89
|
+
* Exported for its own tests: the interesting behaviour of this function is
|
|
90
|
+
* entirely in how it reads `execFile`'s error, and a stub `ProbeRunner` — which
|
|
91
|
+
* is what every other test here uses — cannot reproduce that by construction.
|
|
92
|
+
*/
|
|
93
|
+
export declare const runProbe: ProbeRunner;
|
|
63
94
|
/**
|
|
64
95
|
* Fold whatever an agent calls its install method into the five values the
|
|
65
96
|
* product reasons about.
|
package/dist/agent-versions.js
CHANGED
|
@@ -26,12 +26,30 @@ let cached = null;
|
|
|
26
26
|
export function invalidateAgentVersions() {
|
|
27
27
|
cached = null;
|
|
28
28
|
}
|
|
29
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Exported for its own tests: the interesting behaviour of this function is
|
|
31
|
+
* entirely in how it reads `execFile`'s error, and a stub `ProbeRunner` — which
|
|
32
|
+
* is what every other test here uses — cannot reproduce that by construction.
|
|
33
|
+
*/
|
|
34
|
+
export const runProbe = (file, argv, timeoutMs, options) => new Promise((resolve, reject) => {
|
|
30
35
|
const child = execFile(file, [...argv], { timeout: timeoutMs, maxBuffer: PROBE_MAX_BUFFER }, (error, stdout, stderr) => {
|
|
31
|
-
if (error)
|
|
32
|
-
|
|
33
|
-
|
|
36
|
+
if (!error) {
|
|
37
|
+
resolve({ stdout, stderr });
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
/*
|
|
41
|
+
* `killed` is how Node reports the timeout it was given, and a process
|
|
42
|
+
* we killed produced a TRUNCATED page of JSON — parsing that would be
|
|
43
|
+
* reading half an answer. A missing binary never runs at all and has no
|
|
44
|
+
* numeric exit code, so it falls through to `reject` too.
|
|
45
|
+
*/
|
|
46
|
+
const exited = typeof error.code === 'number';
|
|
47
|
+
const killed = error.killed === true;
|
|
48
|
+
if (options?.tolerateExitCode && exited && !killed) {
|
|
34
49
|
resolve({ stdout, stderr });
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
reject(error);
|
|
35
53
|
});
|
|
36
54
|
// `claude doctor` reads stdin. An `execFile` child inherits an open pipe
|
|
37
55
|
// nobody ever writes to, so without this EOF the probe would sit there
|
|
@@ -117,10 +135,16 @@ export async function measureAgent(runtime, run = runProbe) {
|
|
|
117
135
|
}
|
|
118
136
|
// The install method is a nice-to-have: it decides whether a BUTTON appears,
|
|
119
137
|
// never whether the version is shown. A doctor that failed leaves `unknown`.
|
|
138
|
+
//
|
|
139
|
+
// `tolerateExitCode` — see `ProbeOptions`. The diagnostics command grades the
|
|
140
|
+
// whole machine and fails its own exit code over an unreachable endpoint,
|
|
141
|
+
// while answering our question perfectly on stdout.
|
|
120
142
|
let managedBy = 'unknown';
|
|
121
143
|
try {
|
|
122
144
|
const probe = runtime.managedByProbe;
|
|
123
|
-
const { stdout, stderr } = await run(binary, probe.argv, PROBE_TIMEOUT_MS
|
|
145
|
+
const { stdout, stderr } = await run(binary, probe.argv, PROBE_TIMEOUT_MS, {
|
|
146
|
+
tolerateExitCode: true,
|
|
147
|
+
});
|
|
124
148
|
const output = `${stdout}\n${stderr}`;
|
|
125
149
|
const source = probe.jsonPath ? readJsonPath(stdout, probe.jsonPath) : output;
|
|
126
150
|
managedBy = normalizeManagedBy(source ? matchProbe(probe, source) : null);
|
package/dist/index.js
CHANGED
|
@@ -465,6 +465,12 @@ function runnerCapabilities(apiUrlOverride) {
|
|
|
465
465
|
? ['verify_start', 'verify_status', 'verify_cancel', 'preview_checkout', 'preview_stop']
|
|
466
466
|
: []),
|
|
467
467
|
...(agentInstallEnabled ? ['agent_install'] : []),
|
|
468
|
+
// «Перемерить версии агентов сейчас» — единственный путь, которым машину
|
|
469
|
+
// можно СПРОСИТЬ про версии; всё остальное она присылает сама и по своему
|
|
470
|
+
// расписанию. Объявлено отдельно от `agent_install` и БЕЗ его условия:
|
|
471
|
+
// это чтение, а не установка, и машину, чей владелец запретил ставить из
|
|
472
|
+
// дашборда, спросить о том, что на ней стоит, по-прежнему можно.
|
|
473
|
+
'agent_versions_refresh',
|
|
468
474
|
],
|
|
469
475
|
};
|
|
470
476
|
}
|
package/dist/supervisor.d.ts
CHANGED
|
@@ -244,7 +244,17 @@ export declare class Supervisor {
|
|
|
244
244
|
agent: string;
|
|
245
245
|
from: string | null;
|
|
246
246
|
to: string;
|
|
247
|
-
}
|
|
247
|
+
},
|
|
248
|
+
/**
|
|
249
|
+
* Force a fresh probe on a path the reason alone does not describe.
|
|
250
|
+
*
|
|
251
|
+
* «Перемерить сейчас» sends `measured` — it IS a plain measurement — but it
|
|
252
|
+
* is also the one caller that must not be answered out of the hour-long
|
|
253
|
+
* cache, because a person pressed a button precisely to get past it.
|
|
254
|
+
*/
|
|
255
|
+
options?: {
|
|
256
|
+
force?: boolean;
|
|
257
|
+
}): Promise<AgentVersionsMeasurement | null>;
|
|
248
258
|
private queueVersionChange;
|
|
249
259
|
/**
|
|
250
260
|
* Р13: the agent of a starting session, moved forward in the background.
|
package/dist/supervisor.js
CHANGED
|
@@ -259,14 +259,22 @@ export class Supervisor {
|
|
|
259
259
|
* missing audit lines are not the problem worth solving.
|
|
260
260
|
*/
|
|
261
261
|
static MAX_PENDING_VERSION_CHANGES = 8;
|
|
262
|
-
async publishAgentVersions(reason, changed
|
|
262
|
+
async publishAgentVersions(reason, changed,
|
|
263
|
+
/**
|
|
264
|
+
* Force a fresh probe on a path the reason alone does not describe.
|
|
265
|
+
*
|
|
266
|
+
* «Перемерить сейчас» sends `measured` — it IS a plain measurement — but it
|
|
267
|
+
* is also the one caller that must not be answered out of the hour-long
|
|
268
|
+
* cache, because a person pressed a button precisely to get past it.
|
|
269
|
+
*/
|
|
270
|
+
options) {
|
|
263
271
|
try {
|
|
264
272
|
const measure = this.opts.measureAgentVersions ?? measureAgentVersions;
|
|
265
273
|
// After an install — ours or the auto one — the cached number is a lie,
|
|
266
274
|
// and a stale `at` with it would be dropped by the API's ordering guard
|
|
267
275
|
// together with the audit line the install owes. Forcing it here means a
|
|
268
276
|
// caller cannot forget to invalidate the cache first.
|
|
269
|
-
const measurement = await measure({ force: reason !== 'measured' });
|
|
277
|
+
const measurement = await measure({ force: options?.force ?? reason !== 'measured' });
|
|
270
278
|
// A plain tick carries an earlier install's news rather than replacing it.
|
|
271
279
|
// The API keys the audit line by the measurement's `at`, so the same
|
|
272
280
|
// change arriving under a later `at` is one line, not two.
|
|
@@ -294,11 +302,15 @@ export class Supervisor {
|
|
|
294
302
|
void this.publishAgentVersions('measured');
|
|
295
303
|
}
|
|
296
304
|
}
|
|
305
|
+
// Handed back so a caller can answer with EXACTLY what was published,
|
|
306
|
+
// rather than measuring a second time and hoping the two agree.
|
|
307
|
+
return measurement;
|
|
297
308
|
}
|
|
298
309
|
catch (error) {
|
|
299
310
|
if (changed && reason !== 'measured')
|
|
300
311
|
this.queueVersionChange({ reason, changed });
|
|
301
312
|
log.warn('supervisor: could not report agent versions', { error: String(error) });
|
|
313
|
+
return null;
|
|
302
314
|
}
|
|
303
315
|
}
|
|
304
316
|
queueVersionChange(entry) {
|
|
@@ -5119,6 +5131,43 @@ export class Supervisor {
|
|
|
5119
5131
|
void this.publishAgentVersions('manual', moved);
|
|
5120
5132
|
return;
|
|
5121
5133
|
}
|
|
5134
|
+
/**
|
|
5135
|
+
* «Перемерить версии агентов прямо сейчас» — за кнопкой «Check again».
|
|
5136
|
+
*
|
|
5137
|
+
* Единственный путь, которым машину можно СПРОСИТЬ про версии. Всё
|
|
5138
|
+
* остальное присылает она сама и по своему расписанию: кадр уходит на
|
|
5139
|
+
* каждом переподключении (из кэша), раз в час по таймеру, и свежий —
|
|
5140
|
+
* только после установки, которую сделал сам DevBridge.
|
|
5141
|
+
*
|
|
5142
|
+
* Отсюда и дыра, которую эта команда закрывает: человек обновил агента
|
|
5143
|
+
* руками через `claude update` на машине, раннеру об этом никто не
|
|
5144
|
+
* сказал, и карточка держала старое число до часа. «Новая сессия
|
|
5145
|
+
* чинила» его побочным эффектом — автообновление шло ставить, честно
|
|
5146
|
+
* мерило перед установкой и сбрасывало кэш, — а на машине с
|
|
5147
|
+
* ВЫКЛЮЧЕННЫМ автообновлением не происходило и этого.
|
|
5148
|
+
*
|
|
5149
|
+
* Меряем один раз и отдаём измеренное в ответе, а не только кадром:
|
|
5150
|
+
* кадр и ответ идут по одному сокету, но обрабатываются на той стороне
|
|
5151
|
+
* независимо, и запрос, который вернулся раньше, чем доехала запись,
|
|
5152
|
+
* оставил бы на экране ровно то устаревшее число, ради которого кнопку
|
|
5153
|
+
* и нажали. Кадр при этом всё равно уходит — у него свой путь с
|
|
5154
|
+
* аудитом, и повторная запись с тем же `at` отбрасывается сторожем
|
|
5155
|
+
* порядка на стороне API.
|
|
5156
|
+
*/
|
|
5157
|
+
case 'agent_versions_refresh': {
|
|
5158
|
+
// Один замер на нажатие, и это структура, а не совпадение: публикация
|
|
5159
|
+
// отдаёт то, что измерила, и ответ собирается из НЕГО. Мерить второй
|
|
5160
|
+
// раз «для ответа» значило бы держаться за кэш, который вызывающий не
|
|
5161
|
+
// видит, — а кэш здесь и есть то, мимо чего нажимают кнопку.
|
|
5162
|
+
const snapshot = await this.publishAgentVersions('measured', undefined, { force: true });
|
|
5163
|
+
if (!snapshot) {
|
|
5164
|
+
return void reply({ ok: false, error: 'Could not measure the agents' });
|
|
5165
|
+
}
|
|
5166
|
+
return void reply({
|
|
5167
|
+
ok: true,
|
|
5168
|
+
result: { at: snapshot.at, reason: 'measured', agents: snapshot.agents },
|
|
5169
|
+
});
|
|
5170
|
+
}
|
|
5122
5171
|
/**
|
|
5123
5172
|
* «Would this file reach the agent, and how big is it» (ticket #192).
|
|
5124
5173
|
*
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const RUNNER_VERSION = "0.
|
|
1
|
+
export declare const RUNNER_VERSION = "0.51.0";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/version.js
CHANGED
package/package.json
CHANGED