@bridge_gpt/mcp-server 0.2.68 → 0.2.70
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 +226 -320
- package/build/agent-launchers/claude-executor-adapter.js +17 -4
- package/build/agents.generated.js +4 -4
- package/build/artifact-diff.js +110 -0
- package/build/bin-entrypoint.js +36 -0
- package/build/bridge-permissions.js +650 -98
- package/build/check-update-cli.js +74 -0
- package/build/claude-review-workflow-drift.js +3 -3
- package/build/claude-settings.js +84 -2
- package/build/cli-help.js +7 -1
- package/build/codex-bridge-profile.js +1 -1
- package/build/codex-home.js +9 -1
- package/build/codex-launcher-pin.js +598 -0
- package/build/commands.generated.js +8 -8
- package/build/conductor/bridge-api-client.js +598 -19
- package/build/conductor/bring-up-facts.js +9 -9
- package/build/conductor/doctor.js +466 -18
- package/build/conductor/epic-child-continuation.js +54 -3
- package/build/conductor/epic-implementer-cli.js +132 -34
- package/build/conductor/epic-runtime.js +2 -2
- package/build/conductor/epic-state.js +1 -1
- package/build/conductor/hook-runtime-qualification.js +1 -1
- package/build/conductor/install-doctor.js +4 -2
- package/build/conductor/launch-need-probes.js +337 -0
- package/build/conductor/local-merge.js +241 -56
- package/build/conductor/merge-credential.js +172 -0
- package/build/conductor/merge-execution-eligibility.js +1 -1
- package/build/conductor/merged-child-check.js +95 -0
- package/build/conductor/plan-trim.js +30 -0
- package/build/conductor/pr-ci-producer.js +1 -1
- package/build/conductor/readiness-cli.js +11 -6
- package/build/conductor/readiness-prerequisites.js +41 -0
- package/build/conductor/readiness-sections.js +345 -55
- package/build/conductor/readiness.js +99 -87
- package/build/conductor/recovery-cli.js +606 -21
- package/build/conductor/recovery-operations.js +306 -7
- package/build/conductor/repair-contract.js +47 -2
- package/build/conductor/repository-workflow-remedy.js +8 -6
- package/build/conductor/stored-merge-credential.js +76 -0
- package/build/conductor/stored-merge-token-probe.js +28 -0
- package/build/conductor/temporary-plan.js +50 -0
- package/build/conductor/wait-run-cli.js +759 -0
- package/build/conductor/worker-database-doctor-inspector.js +49 -0
- package/build/conductor-bin.js +2 -2
- package/build/conductor-claude-hook-bin.js +2 -2
- package/build/conductor-claude-hook-removed-stub-bin.js +2 -5
- package/build/conductor-removed-stub-bin.js +2 -5
- package/build/connect-github-api.js +33 -14
- package/build/credential-store.js +356 -13
- package/build/docs.generated.js +27 -14
- package/build/doctor.js +203 -38
- package/build/drive-epic-help.js +212 -0
- package/build/drive-epic.js +1840 -373
- package/build/epic-ci-workflow-drift.js +77 -0
- package/build/epic-ci-workflow-install.js +183 -0
- package/build/epic-ci-workflow.js +157 -0
- package/build/epic-contract-context.js +326 -0
- package/build/epic-implementer-bin.js +131 -120
- package/build/epic-implementer-claude-hook-bin.js +2 -2
- package/build/escalation-reachability.js +134 -0
- package/build/executor/cleanup-report.js +99 -0
- package/build/executor/cli.js +12 -1
- package/build/executor/completion-contract.js +68 -11
- package/build/executor/completion-supervision.js +99 -0
- package/build/executor/delivery-outcome.js +179 -0
- package/build/executor/deps.js +3 -0
- package/build/executor/env.js +71 -0
- package/build/executor/failure-reason-codes.js +126 -0
- package/build/executor/heartbeat.js +38 -0
- package/build/executor/http-client.js +115 -4
- package/build/executor/integration-job.js +298 -5
- package/build/executor/job-errors.js +36 -6
- package/build/executor/job-runner.js +720 -36
- package/build/executor/likely-tests.js +36 -2
- package/build/executor/merge-job.js +145 -2
- package/build/executor/observation.js +73 -4
- package/build/executor/permissions.js +193 -97
- package/build/executor/preflight.js +14 -3
- package/build/executor/process-containment.js +28 -2
- package/build/executor/prompt-spec.js +50 -5
- package/build/executor/rebase-finalization.js +38 -3
- package/build/executor/reconcile-overlap-job.js +35 -7
- package/build/executor/required-work-ledger.js +236 -81
- package/build/executor/resume-pre-spawn.js +38 -1
- package/build/executor/runner.js +91 -3
- package/build/executor/test-clock.js +8 -0
- package/build/executor/types.js +37 -2
- package/build/executor/worker-cli-launcher.js +259 -0
- package/build/executor/worker-command.js +41 -4
- package/build/executor/worker-database-target.js +91 -0
- package/build/executor/worker-database.js +908 -0
- package/build/executor/worker-finalization.js +106 -0
- package/build/executor/worker-guard-hook.js +203 -0
- package/build/executor/worker-resources.js +47 -0
- package/build/executor/worker-verification-guidance.js +15 -2
- package/build/executor/worktree.js +303 -15
- package/build/executor-launch.js +11 -1
- package/build/generated-assets-doctor.js +9 -1
- package/build/generated-assets-health.js +13 -4
- package/build/generated-assets-legacy.js +39 -0
- package/build/generated-assets-paths.js +12 -1
- package/build/generated-assets-repair.js +4 -1
- package/build/generated-assets-test-support.js +12 -0
- package/build/github-remote.js +33 -0
- package/build/github-sign-in.js +62 -0
- package/build/index.js +1214 -465
- package/build/init.js +39 -1
- package/build/install-bridge-conductor-gate.js +78 -0
- package/build/install-bridge-conductor-setup.js +413 -0
- package/build/install-bridge-conductor.js +1226 -267
- package/build/install-bridge.js +629 -350
- package/build/launcher-config-inspection.js +184 -19
- package/build/mcp-host-targets.js +4 -0
- package/build/mcp-identity.js +7 -0
- package/build/mcp-install-state.js +8 -2
- package/build/merge-pull-request.js +6 -0
- package/build/merge-workflow-install.js +763 -345
- package/build/phase-result-artifacts.js +75 -20
- package/build/pipeline-orchestrator.js +247 -4
- package/build/pipelines.generated.js +35 -35
- package/build/plan-discovery.js +15 -2
- package/build/plan-execution-ledger.js +306 -17
- package/build/plan-phase-routing.js +48 -13
- package/build/plane/cli.js +105 -51
- package/build/plane/defaults.js +3 -0
- package/build/plane/plane-freshness.js +294 -0
- package/build/plane/schema-compatibility.js +137 -0
- package/build/plane/shutdown-deps.js +38 -0
- package/build/plane/shutdown.js +83 -8
- package/build/plane/terminal-run-cleanup.js +303 -0
- package/build/printed-remedy-commands.js +26 -4
- package/build/readiness-check.js +6 -2
- package/build/readiness-prerequisites.generated.js +532 -0
- package/build/readme.generated.js +1 -1
- package/build/session-upgrade-notice.js +86 -0
- package/build/setup-epic.js +400 -30
- package/build/shared-resources.generated.js +5 -0
- package/build/start-tickets.js +43 -15
- package/build/test-command-detection.js +247 -0
- package/build/test-command-validation.js +73 -0
- package/build/testing-config-client.js +150 -0
- package/build/ticket-delivery-artifacts.js +258 -0
- package/build/ticket-delivery-enrollment.js +306 -0
- package/build/ticket-delivery.js +205 -0
- package/build/ticket-review-summary.js +14 -0
- package/build/tool-call-advisory.js +162 -0
- package/build/tool-surface-gating.js +28 -89
- package/build/update-check.js +97 -16
- package/build/update-status.js +61 -71
- package/build/upgrade-advice.js +15 -11
- package/build/upgrade-cli.js +360 -20
- package/build/v2-readiness-requirements.js +552 -0
- package/build/version.generated.js +3 -3
- package/build/worker-guard-hook-bin.js +2 -2
- package/build/worktree-core.js +22 -7
- package/docs/BRIDGE-DIRECTORY-README.md +48 -0
- package/docs/EPIC-IMPLEMENTER-INSTALL.md +12 -867
- package/docs/EPIC-IMPLEMENTER.md +104 -63
- package/docs/epic-implementer/getting-started.md +131 -0
- package/docs/epic-implementer/self-hosting.md +133 -0
- package/package.json +3 -3
- package/pipelines/idea-to-ticket.json +1 -1
- package/pipelines/implement-ticket.json +8 -8
- package/pipelines/review-ticket.json +2 -2
- package/design-assets/favicon/favicon-32x32.png +0 -0
- package/design-assets/just-logo-rough-draft.png +0 -0
package/build/update-check.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Update check logic — extracted for testability.
|
|
3
|
-
*
|
|
3
|
+
* `update-status.ts` (the background manager) and `doctor.ts` call checkForUpdate();
|
|
4
|
+
* the synchronous notice paths use `readCachedLatestVersion` and never touch the
|
|
5
|
+
* network.
|
|
4
6
|
*/
|
|
7
|
+
import { readFileSync } from "fs";
|
|
5
8
|
import { writeFile, mkdir, readFile } from "fs/promises";
|
|
6
9
|
import path from "path";
|
|
7
10
|
import os from "os";
|
|
@@ -10,13 +13,51 @@ import { MCP_PACKAGE_NAME } from "./mcp-identity.js";
|
|
|
10
13
|
const CACHE_TTL = 86400000; // 24 hours
|
|
11
14
|
const FETCH_TIMEOUT = 3000;
|
|
12
15
|
const REGISTRY_URL = `https://registry.npmjs.org/${MCP_PACKAGE_NAME}/latest`;
|
|
16
|
+
/** Manual-only override of the latest version, for exercising the notice paths. */
|
|
17
|
+
export const FAKE_LATEST_ENV = "BAPI_MCP_UPDATE_CHECK_FAKE_LATEST";
|
|
18
|
+
const PLAIN_RELEASE_RE = /^\d+\.\d+\.\d+$/;
|
|
13
19
|
export function getCachePath() {
|
|
14
20
|
return path.join(os.homedir(), ".config", ...MCP_PACKAGE_NAME.split("/"), "update-check.json");
|
|
15
21
|
}
|
|
22
|
+
/** True only for a plain `MAJOR.MINOR.PATCH` release string. */
|
|
23
|
+
export function isPlainReleaseVersion(value) {
|
|
24
|
+
return typeof value === "string" && PLAIN_RELEASE_RE.test(value);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Read the manual fake-latest override. Returns the value only when it is a plain
|
|
28
|
+
* release string; anything else (absent, blank, a prerelease, shell text) is
|
|
29
|
+
* ignored and never echoed.
|
|
30
|
+
*/
|
|
31
|
+
export function readFakeLatestOverride(env = process.env) {
|
|
32
|
+
const value = env[FAKE_LATEST_ENV];
|
|
33
|
+
return isPlainReleaseVersion(value) ? value : null;
|
|
34
|
+
}
|
|
35
|
+
/** Compare two digit strings numerically without converting them to Numbers. */
|
|
36
|
+
function compareDigitStrings(a, b) {
|
|
37
|
+
const x = a.replace(/^0+(?=\d)/, "");
|
|
38
|
+
const y = b.replace(/^0+(?=\d)/, "");
|
|
39
|
+
if (x.length !== y.length)
|
|
40
|
+
return x.length > y.length ? 1 : -1;
|
|
41
|
+
if (x === y)
|
|
42
|
+
return 0;
|
|
43
|
+
return x > y ? 1 : -1;
|
|
44
|
+
}
|
|
16
45
|
/**
|
|
17
46
|
* Compare two semver strings. Returns true if `latest` is strictly greater.
|
|
47
|
+
* Valid release strings are compared segment by segment as digit strings, so a
|
|
48
|
+
* segment beyond the safe-integer range keeps its ordering.
|
|
18
49
|
*/
|
|
19
50
|
export function isNewerVersion(current, latest) {
|
|
51
|
+
if (isPlainReleaseVersion(current) && isPlainReleaseVersion(latest)) {
|
|
52
|
+
const c = current.split(".");
|
|
53
|
+
const l = latest.split(".");
|
|
54
|
+
for (let i = 0; i < 3; i++) {
|
|
55
|
+
const order = compareDigitStrings(l[i], c[i]);
|
|
56
|
+
if (order !== 0)
|
|
57
|
+
return order > 0;
|
|
58
|
+
}
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
20
61
|
const c = current.split(".").map(Number);
|
|
21
62
|
const l = latest.split(".").map(Number);
|
|
22
63
|
for (let i = 0; i < 3; i++) {
|
|
@@ -27,28 +68,66 @@ export function isNewerVersion(current, latest) {
|
|
|
27
68
|
}
|
|
28
69
|
return false;
|
|
29
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* Read the latest version the server last cached, synchronously and offline.
|
|
73
|
+
*
|
|
74
|
+
* A valid override wins before the cache is opened. The cache's age is ignored on
|
|
75
|
+
* purpose: a cached latest can only be older than the real latest, so it is a
|
|
76
|
+
* lower bound. It may under-claim an update, but it never claims one that does
|
|
77
|
+
* not exist. Any missing, corrupt, malformed, or unreadable cache returns null.
|
|
78
|
+
*/
|
|
79
|
+
export function readCachedLatestVersion(env = process.env) {
|
|
80
|
+
try {
|
|
81
|
+
const override = readFakeLatestOverride(env);
|
|
82
|
+
if (override)
|
|
83
|
+
return override;
|
|
84
|
+
const cache = JSON.parse(readFileSync(getCachePath(), "utf-8"));
|
|
85
|
+
if (cache === null || typeof cache !== "object" || Array.isArray(cache))
|
|
86
|
+
return null;
|
|
87
|
+
const { lastCheck, latestVersion } = cache;
|
|
88
|
+
if (typeof lastCheck !== "number" || !Number.isFinite(lastCheck))
|
|
89
|
+
return null;
|
|
90
|
+
return isPlainReleaseVersion(latestVersion) ? latestVersion : null;
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
30
96
|
/**
|
|
31
97
|
* Checks the npm registry for a newer version of @bridge_gpt/mcp-server.
|
|
32
98
|
* Results are cached for 24 hours. Returns null if the check fails.
|
|
33
99
|
*/
|
|
34
|
-
export async function checkForUpdate() {
|
|
100
|
+
export async function checkForUpdate(options = {}) {
|
|
35
101
|
try {
|
|
102
|
+
const override = readFakeLatestOverride(options.env ?? process.env);
|
|
103
|
+
if (override) {
|
|
104
|
+
// An override is never cached, so it cannot outlive the manual test.
|
|
105
|
+
return {
|
|
106
|
+
updateAvailable: isNewerVersion(VERSION, override),
|
|
107
|
+
currentVersion: VERSION,
|
|
108
|
+
latestVersion: override,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
const useCache = options.readCache !== false;
|
|
112
|
+
const saveCache = options.writeCache !== false;
|
|
36
113
|
const cachePath = getCachePath();
|
|
37
114
|
const cacheDir = path.dirname(cachePath);
|
|
38
115
|
let latestVersion = null;
|
|
39
116
|
// Try reading cache
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
117
|
+
if (useCache) {
|
|
118
|
+
try {
|
|
119
|
+
const raw = await readFile(cachePath, "utf-8");
|
|
120
|
+
const cache = JSON.parse(raw);
|
|
121
|
+
if (cache &&
|
|
122
|
+
typeof cache.lastCheck === "number" &&
|
|
123
|
+
isPlainReleaseVersion(cache.latestVersion) &&
|
|
124
|
+
Date.now() - cache.lastCheck < CACHE_TTL) {
|
|
125
|
+
latestVersion = cache.latestVersion;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
// Cache missing, corrupt, or stale — will fetch fresh
|
|
48
130
|
}
|
|
49
|
-
}
|
|
50
|
-
catch {
|
|
51
|
-
// Cache missing, corrupt, or stale — will fetch fresh
|
|
52
131
|
}
|
|
53
132
|
// Fetch from registry if no valid cache
|
|
54
133
|
if (!latestVersion) {
|
|
@@ -57,10 +136,12 @@ export async function checkForUpdate() {
|
|
|
57
136
|
try {
|
|
58
137
|
const res = await fetch(REGISTRY_URL, { signal: controller.signal });
|
|
59
138
|
const data = await res.json();
|
|
60
|
-
if (data.version) {
|
|
139
|
+
if (isPlainReleaseVersion(data.version)) {
|
|
61
140
|
latestVersion = data.version;
|
|
62
|
-
|
|
63
|
-
|
|
141
|
+
if (saveCache) {
|
|
142
|
+
await mkdir(cacheDir, { recursive: true });
|
|
143
|
+
await writeFile(cachePath, JSON.stringify({ lastCheck: Date.now(), latestVersion }), "utf-8");
|
|
144
|
+
}
|
|
64
145
|
}
|
|
65
146
|
}
|
|
66
147
|
finally {
|
package/build/update-status.js
CHANGED
|
@@ -1,69 +1,86 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Process-wide
|
|
2
|
+
* Process-wide settled update status (BAPI-728, reshaped by BAPI-1446).
|
|
3
3
|
*
|
|
4
|
-
* The staleness signal already existed
|
|
5
|
-
* start and computed the right answer
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
4
|
+
* The staleness signal already existed: `checkForUpdate()` ran on every server
|
|
5
|
+
* start and computed the right answer, then announced it to a log sink nobody
|
|
6
|
+
* reads. BAPI-1446 moved delivery to surfaces a session actually reads (startup
|
|
7
|
+
* instructions, the first completed tool result, the SessionStart hook). This
|
|
8
|
+
* manager is now only the source of one fact for those surfaces: what the live
|
|
9
|
+
* check concluded, once it has settled.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
* per process (humans), and a `tools/list` advisory (models). It is:
|
|
11
|
+
* The settled-status contract:
|
|
13
12
|
*
|
|
14
13
|
* - **single-flight** — one `checkForUpdate()` per process, cached forever;
|
|
15
|
-
* - **non-blocking** —
|
|
16
|
-
*
|
|
17
|
-
*
|
|
14
|
+
* - **non-blocking** — `start()` returns at once and is never awaited before
|
|
15
|
+
* `connect`; `getStatus()` is synchronous and never waits for the lookup;
|
|
16
|
+
* - **known versus unknown** — `known: true` means a reliable live result
|
|
17
|
+
* settled, including "you are current". Initial, in-flight, disabled,
|
|
18
|
+
* rejected, null, and malformed outcomes are `known: false` and not stale, so
|
|
19
|
+
* the tool-result channel never mistakes "unknown" for "confirmed current"
|
|
20
|
+
* and can fall back to the local cache;
|
|
21
|
+
* - **fail-open** — nothing here throws, and `whenSettled()` never rejects.
|
|
18
22
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
23
|
+
* `stale` is kept for existing production consumers and is only ever true for a
|
|
24
|
+
* reliable newer result.
|
|
25
|
+
*
|
|
26
|
+
* Passing `enabled: false` (BAPI-917) makes the manager permanently unknown from
|
|
27
|
+
* the moment `start()` returns, without invoking the checker, touching the update
|
|
28
|
+
* cache, or making a registry request — used so a hermetic measurement capture
|
|
29
|
+
* cannot be perturbed by runtime update state.
|
|
24
30
|
*/
|
|
25
|
-
import { checkForUpdate } from "./update-check.js";
|
|
26
|
-
import {
|
|
27
|
-
/** The
|
|
28
|
-
const
|
|
31
|
+
import { checkForUpdate, isNewerVersion, isPlainReleaseVersion, readFakeLatestOverride, } from "./update-check.js";
|
|
32
|
+
import { VERSION } from "./version.generated.js";
|
|
33
|
+
/** The unknown conclusion, used for every fail-open branch. */
|
|
34
|
+
const UNKNOWN = { stale: false, known: false };
|
|
29
35
|
/**
|
|
30
36
|
* Build the manager. Nothing runs until `start()` is called, so importing this
|
|
31
37
|
* module never performs I/O.
|
|
32
38
|
*/
|
|
33
39
|
export function createUpdateStatusManager(options = {}) {
|
|
34
|
-
const
|
|
35
|
-
const
|
|
40
|
+
const env = options.env ?? process.env;
|
|
41
|
+
const check = options.check ?? (() => checkForUpdate({ env }));
|
|
36
42
|
const enabled = options.enabled !== false;
|
|
43
|
+
const runningVersion = options.currentVersion ?? VERSION;
|
|
37
44
|
let started = false;
|
|
38
45
|
let settled = null;
|
|
39
46
|
let settledPromise = null;
|
|
40
|
-
let warned = false;
|
|
41
|
-
let listServed = false;
|
|
42
|
-
let lateNotified = false;
|
|
43
47
|
function conclude(result) {
|
|
44
|
-
// Every uncertain shape is
|
|
45
|
-
//
|
|
48
|
+
// Every uncertain shape is unknown. A malformed payload must never produce a
|
|
49
|
+
// claim about a version that may not exist.
|
|
46
50
|
if (!result || typeof result !== "object")
|
|
47
|
-
return
|
|
48
|
-
if (result.updateAvailable !==
|
|
49
|
-
return
|
|
51
|
+
return UNKNOWN;
|
|
52
|
+
if (typeof result.updateAvailable !== "boolean")
|
|
53
|
+
return UNKNOWN;
|
|
50
54
|
const { currentVersion, latestVersion } = result;
|
|
51
|
-
if (
|
|
52
|
-
return
|
|
53
|
-
if (
|
|
54
|
-
return
|
|
55
|
-
return { stale: true, currentVersion, latestVersion };
|
|
55
|
+
if (!isPlainReleaseVersion(currentVersion))
|
|
56
|
+
return UNKNOWN;
|
|
57
|
+
if (!isPlainReleaseVersion(latestVersion))
|
|
58
|
+
return UNKNOWN;
|
|
59
|
+
return { stale: result.updateAvailable, known: true, currentVersion, latestVersion };
|
|
56
60
|
}
|
|
57
61
|
function start() {
|
|
58
62
|
if (started)
|
|
59
63
|
return;
|
|
60
64
|
started = true;
|
|
61
65
|
if (!enabled) {
|
|
62
|
-
// Disabled: settle immediately, without ever invoking `check
|
|
63
|
-
//
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
66
|
+
// Disabled: settle immediately, without ever invoking `check`. `whenSettled()`
|
|
67
|
+
// stays usable — it just resolves to the same already-known conclusion.
|
|
68
|
+
settled = UNKNOWN;
|
|
69
|
+
settledPromise = Promise.resolve(UNKNOWN);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
// A valid fake-latest override settles at once, before any filesystem or
|
|
73
|
+
// registry work, exactly as `checkForUpdate` honors it.
|
|
74
|
+
const override = readFakeLatestOverride(env);
|
|
75
|
+
if (override && isPlainReleaseVersion(runningVersion)) {
|
|
76
|
+
const status = {
|
|
77
|
+
stale: isNewerVersion(runningVersion, override),
|
|
78
|
+
known: true,
|
|
79
|
+
currentVersion: runningVersion,
|
|
80
|
+
latestVersion: override,
|
|
81
|
+
};
|
|
82
|
+
settled = status;
|
|
83
|
+
settledPromise = Promise.resolve(status);
|
|
67
84
|
return;
|
|
68
85
|
}
|
|
69
86
|
settledPromise = (async () => {
|
|
@@ -72,46 +89,19 @@ export function createUpdateStatusManager(options = {}) {
|
|
|
72
89
|
status = conclude(await check());
|
|
73
90
|
}
|
|
74
91
|
catch {
|
|
75
|
-
status =
|
|
92
|
+
status = UNKNOWN; // `checkForUpdate` is contracted not to reject; belt and braces.
|
|
76
93
|
}
|
|
77
94
|
settled = status;
|
|
78
|
-
if (status.stale && !warned) {
|
|
79
|
-
warned = true;
|
|
80
|
-
// stderr, never stdout: stdout is the JSON-RPC frame channel and any
|
|
81
|
-
// stray write there corrupts the protocol stream.
|
|
82
|
-
warn(formatUpdateAdvice(status.currentVersion, status.latestVersion));
|
|
83
|
-
if (listServed && !lateNotified) {
|
|
84
|
-
lateNotified = true;
|
|
85
|
-
try {
|
|
86
|
-
options.onLateStale?.();
|
|
87
|
-
}
|
|
88
|
-
catch {
|
|
89
|
-
// A notification failure must never affect server operation.
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
95
|
return status;
|
|
94
96
|
})();
|
|
95
97
|
}
|
|
96
98
|
return {
|
|
97
99
|
start,
|
|
98
|
-
getStatus: () => settled ??
|
|
100
|
+
getStatus: () => settled ?? UNKNOWN,
|
|
99
101
|
whenSettled: async () => {
|
|
100
102
|
if (!started)
|
|
101
103
|
start();
|
|
102
|
-
return (await settledPromise) ??
|
|
103
|
-
},
|
|
104
|
-
markListServed: () => {
|
|
105
|
-
listServed = true;
|
|
104
|
+
return (await settledPromise) ?? UNKNOWN;
|
|
106
105
|
},
|
|
107
106
|
};
|
|
108
107
|
}
|
|
109
|
-
/**
|
|
110
|
-
* Produce the model-visible advisory for a settled status, or null when the
|
|
111
|
-
* status is not (yet) known-stale. Pure — safe to call on every list request.
|
|
112
|
-
*/
|
|
113
|
-
export function updateAdvisoryFor(status) {
|
|
114
|
-
if (!status.stale || !status.currentVersion || !status.latestVersion)
|
|
115
|
-
return null;
|
|
116
|
-
return formatToolSurfaceUpdateAdvisory(status.currentVersion, status.latestVersion);
|
|
117
|
-
}
|
package/build/upgrade-advice.js
CHANGED
|
@@ -29,20 +29,24 @@ function sanitizeVersion(value) {
|
|
|
29
29
|
return typeof value === "string" && SAFE_VERSION_RE.test(value) ? value : "unknown";
|
|
30
30
|
}
|
|
31
31
|
/**
|
|
32
|
-
* Format the
|
|
33
|
-
*
|
|
32
|
+
* Format the human-facing staleness line (stderr, terminal, `doctor`, and the
|
|
33
|
+
* `ping` advice item). It names both versions and the slash command, and carries
|
|
34
|
+
* no instruction for a model; the model-directed text is
|
|
35
|
+
* {@link formatSessionUpgradeNotice}.
|
|
34
36
|
*/
|
|
35
37
|
export function formatUpdateAdvice(currentVersion, latestVersion) {
|
|
36
|
-
return (`Bridge
|
|
37
|
-
`latest ${sanitizeVersion(latestVersion)} — run
|
|
38
|
+
return (`Bridge MCP: installed ${sanitizeVersion(currentVersion)}, ` +
|
|
39
|
+
`latest ${sanitizeVersion(latestVersion)} — upgrade available — run /upgrade-bridge`);
|
|
38
40
|
}
|
|
39
41
|
/**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
42
|
+
* Format the one-line, plain-text notice delivered to a model session (startup
|
|
43
|
+
* instructions, the first completed tool result, the SessionStart hook). It tells
|
|
44
|
+
* the model to inform the user, offer the upgrade, and run `/upgrade-bridge`
|
|
45
|
+
* only if they say yes. Both versions go through `sanitizeVersion`, so no
|
|
46
|
+
* caller can interpolate arbitrary text.
|
|
43
47
|
*/
|
|
44
|
-
export function
|
|
45
|
-
return (`
|
|
46
|
-
`
|
|
47
|
-
|
|
48
|
+
export function formatSessionUpgradeNotice(current, latest) {
|
|
49
|
+
return (`Bridge MCP upgrade available: this session is running ${sanitizeVersion(current)} ` +
|
|
50
|
+
`and ${sanitizeVersion(latest)} is published. Tell the user an upgrade is available, ` +
|
|
51
|
+
"offer to run /upgrade-bridge, and run it only if they say yes.");
|
|
48
52
|
}
|