@uniqbit/mate-core 0.17.0-canary.3 → 0.17.0-canary.6
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/package.json
CHANGED
|
@@ -8,7 +8,9 @@ import { validateGuidanceData, type MateGuidanceFile } from "../../../runtime/gu
|
|
|
8
8
|
import {
|
|
9
9
|
getOpenCodePluginPackageReference,
|
|
10
10
|
isMateOpenCodePluginReference,
|
|
11
|
+
OPENCODE_PLUGIN_PACKAGE_NAME,
|
|
11
12
|
} from "../../opencode-plugin-package";
|
|
13
|
+
import { installedVersionAt, isPreinstalledPluginPath } from "../../preinstalled-plugins";
|
|
12
14
|
import {
|
|
13
15
|
getOpenCodePluginReferences,
|
|
14
16
|
mergeOpenCodeConfigContent,
|
|
@@ -129,6 +131,27 @@ export class OpenCodeAdapter extends LaunchAdapter {
|
|
|
129
131
|
return [];
|
|
130
132
|
}
|
|
131
133
|
|
|
134
|
+
// Setup writes one of two spellings, and both are current: the published
|
|
135
|
+
// spec on an ordinary workstation, and an absolute path into the
|
|
136
|
+
// machine-local workspace where the distribution supplied an installed
|
|
137
|
+
// copy. Comparing only against the spec would refuse the launch on exactly
|
|
138
|
+
// the deployments preinstalled binding exists for.
|
|
139
|
+
//
|
|
140
|
+
// A bound reference carries its version in the package it points at rather
|
|
141
|
+
// than in the string, so staleness is read off disk. It is still caught,
|
|
142
|
+
// and now names the version actually installed.
|
|
143
|
+
const expectedVersion = expectedPluginReference.slice(OPENCODE_PLUGIN_PACKAGE_NAME.length + 1);
|
|
144
|
+
const bound = mateReferences.find((reference) =>
|
|
145
|
+
isPreinstalledPluginPath(reference, OPENCODE_PLUGIN_PACKAGE_NAME),
|
|
146
|
+
);
|
|
147
|
+
if (bound !== undefined) {
|
|
148
|
+
const version = await installedVersionAt(bound);
|
|
149
|
+
if (version === expectedVersion) return [];
|
|
150
|
+
return [
|
|
151
|
+
`Stale Mate plugin package reference in ${configFile}: it points at ${bound}, which is ${version ?? "not installed"} rather than ${expectedVersion}.`,
|
|
152
|
+
];
|
|
153
|
+
}
|
|
154
|
+
|
|
132
155
|
if (mateReferences.length > 0) {
|
|
133
156
|
return [
|
|
134
157
|
`Stale Mate plugin package reference in ${configFile}: found ${mateReferences.join(", ")}.`,
|
|
@@ -36,6 +36,25 @@ export function isPreinstalledPluginPath(entry: unknown, packageName: string): b
|
|
|
36
36
|
return normalized.endsWith(`/${packageName}`);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
/**
|
|
40
|
+
* The version behind a reference bound to an installed copy.
|
|
41
|
+
*
|
|
42
|
+
* A published reference carries its version in the string; a bound one carries
|
|
43
|
+
* it in the package it points at, so staleness has to be read off disk. Shared
|
|
44
|
+
* by every site that validates a bound reference, so they cannot disagree about
|
|
45
|
+
* what "stale" means.
|
|
46
|
+
*/
|
|
47
|
+
export async function installedVersionAt(packageRoot: string): Promise<string | undefined> {
|
|
48
|
+
try {
|
|
49
|
+
const manifest = JSON.parse(
|
|
50
|
+
await fs.readFile(path.join(packageRoot, "package.json"), "utf8"),
|
|
51
|
+
) as { version?: string };
|
|
52
|
+
return manifest.version;
|
|
53
|
+
} catch {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
39
58
|
export class PreinstalledPluginMismatchError extends Error {}
|
|
40
59
|
|
|
41
60
|
/**
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
validateContextModePackage,
|
|
12
12
|
} from "../../../lib/context-mode-package";
|
|
13
13
|
import { warmOpenCodePackageCache } from "../../../lib/opencode-plugin-package";
|
|
14
|
+
import { installedVersionAt, isPreinstalledPluginPath } from "../../../lib/preinstalled-plugins";
|
|
14
15
|
import type {
|
|
15
16
|
CapabilityPlugin,
|
|
16
17
|
LaunchPreflightContext,
|
|
@@ -70,17 +71,43 @@ async function assertNoMcpConflict(ctx: SetupContext, provider: "claude" | "open
|
|
|
70
71
|
}
|
|
71
72
|
}
|
|
72
73
|
|
|
74
|
+
/**
|
|
75
|
+
* Both spellings of a current reference are accepted, because the Runtime
|
|
76
|
+
* Surface writes either one: the published spec on an ordinary workstation, and
|
|
77
|
+
* an absolute path into the machine-local workspace where the distribution
|
|
78
|
+
* supplied an installed copy. Accepting only the first would refuse the launch
|
|
79
|
+
* on exactly the deployments preinstalled binding exists for.
|
|
80
|
+
*
|
|
81
|
+
* Staleness is still caught in both cases — a published reference by its
|
|
82
|
+
* version, a bound one by the version of the copy it points at — so this is not
|
|
83
|
+
* the looser check it might look like.
|
|
84
|
+
*/
|
|
73
85
|
async function validateOpenCodeReferences(ctx: LaunchPreflightContext): Promise<string[]> {
|
|
74
86
|
const expected = getContextModePackageReference();
|
|
75
87
|
const diagnostics: string[] = [];
|
|
76
88
|
for (const name of ["opencode.json", "tui.json"]) {
|
|
77
89
|
const configPath = path.join(ctx.companionPath, ".opencode", name);
|
|
78
90
|
const { config } = await readOpenCodeConfig(configPath);
|
|
79
|
-
|
|
91
|
+
const references = getOpenCodePluginReferences(config);
|
|
92
|
+
if (references.includes(expected)) continue;
|
|
93
|
+
|
|
94
|
+
const bound = references.find(
|
|
95
|
+
(reference): reference is string =>
|
|
96
|
+
typeof reference === "string" &&
|
|
97
|
+
isPreinstalledPluginPath(reference, CONTEXT_MODE_PACKAGE_NAME),
|
|
98
|
+
);
|
|
99
|
+
if (bound !== undefined) {
|
|
100
|
+
const version = await installedVersionAt(bound);
|
|
101
|
+
if (version === CONTEXT_MODE_VERSION) continue;
|
|
80
102
|
diagnostics.push(
|
|
81
|
-
`
|
|
103
|
+
`Stale context-mode package reference in ${configPath}; it points at ${bound}, which is ${version ?? "not installed"} rather than ${CONTEXT_MODE_VERSION}.`,
|
|
82
104
|
);
|
|
105
|
+
continue;
|
|
83
106
|
}
|
|
107
|
+
|
|
108
|
+
diagnostics.push(
|
|
109
|
+
`Missing or stale context-mode package reference in ${configPath}; expected ${expected}.`,
|
|
110
|
+
);
|
|
84
111
|
}
|
|
85
112
|
return diagnostics;
|
|
86
113
|
}
|
|
@@ -6,7 +6,7 @@ import { promisify } from "node:util";
|
|
|
6
6
|
import { getOpenSpecSchemaSelection } from "../../../lib/orchestrator/setup-compatibilities";
|
|
7
7
|
import { getWrapperBinPath } from "../../../lib/package-paths";
|
|
8
8
|
import { fetchPublicPackageVersion } from "../../../lib/public-npm";
|
|
9
|
-
import { isNewer } from "../../../lib/update-checker";
|
|
9
|
+
import { isNewer, isPinnedDeployment } from "../../../lib/update-checker";
|
|
10
10
|
import { FRAMEWORK_NAME } from "../../../framework";
|
|
11
11
|
import { confirm } from "../../../cli/confirm";
|
|
12
12
|
import type { CapabilityPlugin, RuntimeContributionsByRuntime, SetupContext } from "../plugin";
|
|
@@ -63,6 +63,7 @@ export interface OpenSpecPluginDeps {
|
|
|
63
63
|
wrapperBinPath?: () => string;
|
|
64
64
|
getInstalledVersion?: (pathValue: string) => Promise<string | undefined>;
|
|
65
65
|
fetchLatestVersion?: () => Promise<string | undefined>;
|
|
66
|
+
isPinnedDeployment?: () => boolean;
|
|
66
67
|
}
|
|
67
68
|
|
|
68
69
|
const OPEN_SPEC_SUPPORTED_TOOLS = Object.keys(OPENSPEC_TOOL_DIRS) as OpenSpecTool[];
|
|
@@ -170,10 +171,17 @@ async function updateOpenSpecIfOutdated(
|
|
|
170
171
|
deps: Required<
|
|
171
172
|
Pick<
|
|
172
173
|
OpenSpecPluginDeps,
|
|
173
|
-
|
|
174
|
+
| "installCommand"
|
|
175
|
+
| "pathValue"
|
|
176
|
+
| "getInstalledVersion"
|
|
177
|
+
| "fetchLatestVersion"
|
|
178
|
+
| "isPinnedDeployment"
|
|
174
179
|
>
|
|
175
180
|
>,
|
|
176
181
|
): Promise<void> {
|
|
182
|
+
/** Pinned artifacts change tool versions by replacement, never reconciliation. */
|
|
183
|
+
if (deps.isPinnedDeployment()) return;
|
|
184
|
+
|
|
177
185
|
const installed = await deps.getInstalledVersion(deps.pathValue());
|
|
178
186
|
if (!installed) return;
|
|
179
187
|
|
|
@@ -204,6 +212,7 @@ async function ensureOpenSpecInstalled(
|
|
|
204
212
|
| "pathValue"
|
|
205
213
|
| "getInstalledVersion"
|
|
206
214
|
| "fetchLatestVersion"
|
|
215
|
+
| "isPinnedDeployment"
|
|
207
216
|
>
|
|
208
217
|
>,
|
|
209
218
|
): Promise<boolean> {
|
|
@@ -326,6 +335,7 @@ export function createOpenspecPlugin(deps: OpenSpecPluginDeps = {}): CapabilityP
|
|
|
326
335
|
wrapperBinPath: deps.wrapperBinPath ?? getWrapperBinPath,
|
|
327
336
|
getInstalledVersion: deps.getInstalledVersion ?? defaultGetInstalledVersion,
|
|
328
337
|
fetchLatestVersion: deps.fetchLatestVersion ?? defaultFetchLatestVersion,
|
|
338
|
+
isPinnedDeployment: deps.isPinnedDeployment ?? (() => isPinnedDeployment()),
|
|
329
339
|
};
|
|
330
340
|
|
|
331
341
|
return {
|