@modelprofile.com/mcp-crossharness 5.5.0 → 5.6.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_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/functions.codexversion.d.ts +39 -0
- package/dist_ts/functions.codexversion.js +70 -0
- package/dist_ts/index.d.ts +1 -0
- package/dist_ts/index.js +2 -1
- package/package.json +3 -3
- package/readme.md +32 -0
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/functions.codexversion.ts +88 -0
- package/ts/index.ts +1 -0
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@modelprofile.com/mcp-crossharness',
|
|
6
|
-
version: '5.
|
|
6
|
+
version: '5.6.0',
|
|
7
7
|
description: 'Connection-first MCP server for explicit OpenCode HTTP and Codex WebSocket session servers'
|
|
8
8
|
};
|
|
9
9
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSxvQ0FBb0M7SUFDMUMsT0FBTyxFQUFFLE9BQU87SUFDaEIsV0FBVyxFQUFFLDRGQUE0RjtDQUMxRyxDQUFBIn0=
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The first Codex release whose `app-server --listen unix://PATH` publishes the alias
|
|
3
|
+
* `codexUnixSocketTarget` and `observeCodexUnixSocket` verify. Earlier releases bind PATH
|
|
4
|
+
* directly, which this package refuses, so a consumer that relies on that contract refuses
|
|
5
|
+
* them up front, by version.
|
|
6
|
+
*/
|
|
7
|
+
export declare const CODEX_UNIX_SOCKET_MINIMUM_VERSION = "0.156.0";
|
|
8
|
+
/** A Codex release number as Codex prints it: `MAJOR.MINOR.PATCH` with an optional prerelease. */
|
|
9
|
+
export interface ICodexVersion {
|
|
10
|
+
major: number;
|
|
11
|
+
minor: number;
|
|
12
|
+
patch: number;
|
|
13
|
+
/** The prerelease after `-`, such as `alpha.2`, or null for a release. */
|
|
14
|
+
prerelease: string | null;
|
|
15
|
+
/** The exact text that was parsed. */
|
|
16
|
+
text: string;
|
|
17
|
+
}
|
|
18
|
+
/** `unreadable`: not a Codex release number. `below_minimum`: older than the required release. */
|
|
19
|
+
export type TCodexVersionUnsupportedReason = 'unreadable' | 'below_minimum';
|
|
20
|
+
/** A Codex whose version cannot be read, or is older than the release a consumer requires. */
|
|
21
|
+
export declare class CodexVersionUnsupportedError extends Error {
|
|
22
|
+
readonly reason: TCodexVersionUnsupportedReason;
|
|
23
|
+
/** The version text that was checked; null when none was reported. */
|
|
24
|
+
readonly version: string | null;
|
|
25
|
+
readonly minimum: string;
|
|
26
|
+
constructor(reasonArg: TCodexVersionUnsupportedReason, versionArg: string | null, minimumArg: string);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Parses exactly one Codex release number, such as `0.157.1` or `0.158.0-alpha.2`. Anything else
|
|
30
|
+
* returns null: a prefix such as `codex-cli `, surrounding whitespace, build metadata, leading
|
|
31
|
+
* zeros or components beyond the safe integer range.
|
|
32
|
+
*/
|
|
33
|
+
export declare const parseCodexVersion: (textArg: string) => ICodexVersion | null;
|
|
34
|
+
/**
|
|
35
|
+
* Returns the parsed version when it is `minimumArg` or newer, and throws
|
|
36
|
+
* `CodexVersionUnsupportedError` otherwise. A prerelease of a newer release is accepted; a
|
|
37
|
+
* prerelease of the minimum itself is older than the minimum. The minimum must be a release.
|
|
38
|
+
*/
|
|
39
|
+
export declare const requireCodexVersion: (versionArg: string | null | undefined, minimumArg?: string) => ICodexVersion;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The first Codex release whose `app-server --listen unix://PATH` publishes the alias
|
|
3
|
+
* `codexUnixSocketTarget` and `observeCodexUnixSocket` verify. Earlier releases bind PATH
|
|
4
|
+
* directly, which this package refuses, so a consumer that relies on that contract refuses
|
|
5
|
+
* them up front, by version.
|
|
6
|
+
*/
|
|
7
|
+
export const CODEX_UNIX_SOCKET_MINIMUM_VERSION = '0.156.0';
|
|
8
|
+
/** A Codex whose version cannot be read, or is older than the release a consumer requires. */
|
|
9
|
+
export class CodexVersionUnsupportedError extends Error {
|
|
10
|
+
reason;
|
|
11
|
+
/** The version text that was checked; null when none was reported. */
|
|
12
|
+
version;
|
|
13
|
+
minimum;
|
|
14
|
+
constructor(reasonArg, versionArg, minimumArg) {
|
|
15
|
+
super(reasonArg === 'unreadable'
|
|
16
|
+
? `Codex did not report a readable version; Codex ${minimumArg} or newer is required`
|
|
17
|
+
: `Codex ${versionArg} is older than the required Codex ${minimumArg}`);
|
|
18
|
+
this.name = 'CodexVersionUnsupportedError';
|
|
19
|
+
this.reason = reasonArg;
|
|
20
|
+
this.version = versionArg;
|
|
21
|
+
this.minimum = minimumArg;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const maximumVersionLength = 64;
|
|
25
|
+
const numericPart = '(0|[1-9]\\d*)';
|
|
26
|
+
const prereleasePart = '(?:0|[1-9]\\d*|\\d*[A-Za-z-][0-9A-Za-z-]*)';
|
|
27
|
+
const versionPattern = new RegExp(`^${numericPart}\\.${numericPart}\\.${numericPart}(?:-(${prereleasePart}(?:\\.${prereleasePart})*))?$`);
|
|
28
|
+
/**
|
|
29
|
+
* Parses exactly one Codex release number, such as `0.157.1` or `0.158.0-alpha.2`. Anything else
|
|
30
|
+
* returns null: a prefix such as `codex-cli `, surrounding whitespace, build metadata, leading
|
|
31
|
+
* zeros or components beyond the safe integer range.
|
|
32
|
+
*/
|
|
33
|
+
export const parseCodexVersion = (textArg) => {
|
|
34
|
+
if (typeof textArg !== 'string' || textArg.length > maximumVersionLength)
|
|
35
|
+
return null;
|
|
36
|
+
const match = versionPattern.exec(textArg);
|
|
37
|
+
if (!match)
|
|
38
|
+
return null;
|
|
39
|
+
const [major, minor, patch] = [match[1], match[2], match[3]].map(Number);
|
|
40
|
+
if (![major, minor, patch].every(Number.isSafeInteger))
|
|
41
|
+
return null;
|
|
42
|
+
return { major, minor, patch, prerelease: match[4] ?? null, text: textArg };
|
|
43
|
+
};
|
|
44
|
+
/** Orders by release number; a prerelease precedes the release of the same number. */
|
|
45
|
+
const isBelow = (versionArg, minimumArg) => {
|
|
46
|
+
for (const key of ['major', 'minor', 'patch']) {
|
|
47
|
+
if (versionArg[key] !== minimumArg[key])
|
|
48
|
+
return versionArg[key] < minimumArg[key];
|
|
49
|
+
}
|
|
50
|
+
return versionArg.prerelease !== null;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Returns the parsed version when it is `minimumArg` or newer, and throws
|
|
54
|
+
* `CodexVersionUnsupportedError` otherwise. A prerelease of a newer release is accepted; a
|
|
55
|
+
* prerelease of the minimum itself is older than the minimum. The minimum must be a release.
|
|
56
|
+
*/
|
|
57
|
+
export const requireCodexVersion = (versionArg, minimumArg = CODEX_UNIX_SOCKET_MINIMUM_VERSION) => {
|
|
58
|
+
const minimum = parseCodexVersion(minimumArg);
|
|
59
|
+
if (!minimum || minimum.prerelease !== null) {
|
|
60
|
+
throw new Error('Codex minimum version must be a release number such as 0.156.0');
|
|
61
|
+
}
|
|
62
|
+
const reported = typeof versionArg === 'string' ? versionArg : null;
|
|
63
|
+
const version = reported === null ? null : parseCodexVersion(reported);
|
|
64
|
+
if (!version)
|
|
65
|
+
throw new CodexVersionUnsupportedError('unreadable', reported, minimumArg);
|
|
66
|
+
if (isBelow(version, minimum))
|
|
67
|
+
throw new CodexVersionUnsupportedError('below_minimum', reported, minimumArg);
|
|
68
|
+
return version;
|
|
69
|
+
};
|
|
70
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZnVuY3Rpb25zLmNvZGV4dmVyc2lvbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL2Z1bmN0aW9ucy5jb2RleHZlcnNpb24udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7O0dBS0c7QUFDSCxNQUFNLENBQUMsTUFBTSxpQ0FBaUMsR0FBRyxTQUFTLENBQUM7QUFnQjNELDhGQUE4RjtBQUM5RixNQUFNLE9BQU8sNEJBQTZCLFNBQVEsS0FBSztJQUNyQyxNQUFNLENBQWlDO0lBQ3ZELHNFQUFzRTtJQUN0RCxPQUFPLENBQWdCO0lBQ3ZCLE9BQU8sQ0FBUztJQUVoQyxZQUFZLFNBQXlDLEVBQUUsVUFBeUIsRUFBRSxVQUFrQjtRQUNsRyxLQUFLLENBQUMsU0FBUyxLQUFLLFlBQVk7WUFDOUIsQ0FBQyxDQUFDLGtEQUFrRCxVQUFVLHVCQUF1QjtZQUNyRixDQUFDLENBQUMsU0FBUyxVQUFVLHFDQUFxQyxVQUFVLEVBQUUsQ0FBQyxDQUFDO1FBQzFFLElBQUksQ0FBQyxJQUFJLEdBQUcsOEJBQThCLENBQUM7UUFDM0MsSUFBSSxDQUFDLE1BQU0sR0FBRyxTQUFTLENBQUM7UUFDeEIsSUFBSSxDQUFDLE9BQU8sR0FBRyxVQUFVLENBQUM7UUFDMUIsSUFBSSxDQUFDLE9BQU8sR0FBRyxVQUFVLENBQUM7SUFDNUIsQ0FBQztDQUNGO0FBRUQsTUFBTSxvQkFBb0IsR0FBRyxFQUFFLENBQUM7QUFDaEMsTUFBTSxXQUFXLEdBQUcsZUFBZSxDQUFDO0FBQ3BDLE1BQU0sY0FBYyxHQUFHLDRDQUE0QyxDQUFDO0FBQ3BFLE1BQU0sY0FBYyxHQUFHLElBQUksTUFBTSxDQUMvQixJQUFJLFdBQVcsTUFBTSxXQUFXLE1BQU0sV0FBVyxRQUFRLGNBQWMsU0FBUyxjQUFjLFFBQVEsQ0FDdkcsQ0FBQztBQUVGOzs7O0dBSUc7QUFDSCxNQUFNLENBQUMsTUFBTSxpQkFBaUIsR0FBRyxDQUFDLE9BQWUsRUFBd0IsRUFBRTtJQUN6RSxJQUFJLE9BQU8s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
|
package/dist_ts/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export * from './interfaces.js';
|
|
2
2
|
export * from './classes.codexappserverclient.js';
|
|
3
3
|
export * from './functions.codexunixsocket.js';
|
|
4
|
+
export * from './functions.codexversion.js';
|
|
4
5
|
export * from './classes.connectionregistry.js';
|
|
5
6
|
export * from './classes.dispatchregistry.js';
|
|
6
7
|
export * from './classes.mcptoolregistrar.js';
|
package/dist_ts/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export * from './interfaces.js';
|
|
2
2
|
export * from './classes.codexappserverclient.js';
|
|
3
3
|
export * from './functions.codexunixsocket.js';
|
|
4
|
+
export * from './functions.codexversion.js';
|
|
4
5
|
export * from './classes.connectionregistry.js';
|
|
5
6
|
export * from './classes.dispatchregistry.js';
|
|
6
7
|
export * from './classes.mcptoolregistrar.js';
|
|
@@ -9,4 +10,4 @@ import { CrossHarnessMcpServer } from './classes.mcpserver.js';
|
|
|
9
10
|
export const runCli = async () => {
|
|
10
11
|
await new CrossHarnessMcpServer().start();
|
|
11
12
|
};
|
|
12
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
13
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLGlCQUFpQixDQUFDO0FBQ2hDLGNBQWMsbUNBQW1DLENBQUM7QUFDbEQsY0FBYyxnQ0FBZ0MsQ0FBQztBQUMvQyxjQUFjLDZCQUE2QixDQUFDO0FBQzVDLGNBQWMsaUNBQWlDLENBQUM7QUFDaEQsY0FBYywrQkFBK0IsQ0FBQztBQUM5QyxjQUFjLCtCQUErQixDQUFDO0FBQzlDLGNBQWMsd0JBQXdCLENBQUM7QUFFdkMsT0FBTyxFQUFFLHFCQUFxQixFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFFL0QsTUFBTSxDQUFDLE1BQU0sTUFBTSxHQUFHLEtBQUssSUFBbUIsRUFBRTtJQUM5QyxNQUFNLElBQUkscUJBQXFCLEVBQUUsQ0FBQyxLQUFLLEVBQUUsQ0FBQztBQUM1QyxDQUFDLENBQUMifQ==
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@modelprofile.com/mcp-crossharness",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.6.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Connection-first MCP server for explicit OpenCode HTTP and Codex WebSocket session servers",
|
|
6
6
|
"main": "dist_ts/index.js",
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"@git.zone/tsbuild": "^5.0.0",
|
|
27
27
|
"@git.zone/tsrun": "^3.0.0",
|
|
28
|
-
"@git.zone/tstest": "^6.
|
|
29
|
-
"@types/node": "^26.6.
|
|
28
|
+
"@git.zone/tstest": "^6.3.0",
|
|
29
|
+
"@types/node": "^26.6.2",
|
|
30
30
|
"@types/ws": "^8.18.1"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
package/readme.md
CHANGED
|
@@ -247,6 +247,38 @@ plain `Error`s with these exact messages:
|
|
|
247
247
|
|
|
248
248
|
Filesystem errors other than a missing path propagate unchanged.
|
|
249
249
|
|
|
250
|
+
### Codex version floor
|
|
251
|
+
|
|
252
|
+
A consumer that relies on the alias contract refuses an older Codex by version before it
|
|
253
|
+
starts one. `CODEX_UNIX_SOCKET_MINIMUM_VERSION` is `'0.156.0'`, the first release that
|
|
254
|
+
publishes the alias, and `requireCodexVersion` checks a version against it:
|
|
255
|
+
|
|
256
|
+
```typescript
|
|
257
|
+
import { CodexVersionUnsupportedError, requireCodexVersion } from '@modelprofile.com/mcp-crossharness';
|
|
258
|
+
|
|
259
|
+
await client.connect();
|
|
260
|
+
requireCodexVersion(client.serverVersion); // throws CodexVersionUnsupportedError below 0.156.0
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
`parseCodexVersion(text)` reads exactly one release number, `MAJOR.MINOR.PATCH` with an
|
|
264
|
+
optional prerelease such as `0.158.0-alpha.2`, and returns `{ major, minor, patch, prerelease, text }`,
|
|
265
|
+
or `null` for anything else: a prefix such as `codex-cli `, surrounding whitespace, build
|
|
266
|
+
metadata, leading zeros or a component beyond the safe integer range. Strip Codex's own
|
|
267
|
+
prefix before parsing.
|
|
268
|
+
|
|
269
|
+
`requireCodexVersion(version, minimum?)` returns the parsed version when it is the minimum or
|
|
270
|
+
newer. The minimum defaults to `CODEX_UNIX_SOCKET_MINIMUM_VERSION`; a consumer that needs a
|
|
271
|
+
later release for its own reasons passes that release, which must be a release number and not
|
|
272
|
+
a prerelease. A prerelease of a newer release is accepted; a prerelease of the minimum itself
|
|
273
|
+
is older than the minimum. Otherwise it throws `CodexVersionUnsupportedError`, whose `reason`
|
|
274
|
+
is `'unreadable'` (no version, or not a release number) or `'below_minimum'`, with the checked
|
|
275
|
+
`version` (null when none was reported) and the `minimum`:
|
|
276
|
+
|
|
277
|
+
| Message | Reason |
|
|
278
|
+
| --- | --- |
|
|
279
|
+
| `Codex did not report a readable version; Codex <minimum> or newer is required` | `unreadable` |
|
|
280
|
+
| `Codex <version> is older than the required Codex <minimum>` | `below_minimum` |
|
|
281
|
+
|
|
250
282
|
### Composable tool registration
|
|
251
283
|
|
|
252
284
|
`CrossHarnessMcpToolRegistrar` registers the same nine Crossharness tools used by the standalone
|
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The first Codex release whose `app-server --listen unix://PATH` publishes the alias
|
|
3
|
+
* `codexUnixSocketTarget` and `observeCodexUnixSocket` verify. Earlier releases bind PATH
|
|
4
|
+
* directly, which this package refuses, so a consumer that relies on that contract refuses
|
|
5
|
+
* them up front, by version.
|
|
6
|
+
*/
|
|
7
|
+
export const CODEX_UNIX_SOCKET_MINIMUM_VERSION = '0.156.0';
|
|
8
|
+
|
|
9
|
+
/** A Codex release number as Codex prints it: `MAJOR.MINOR.PATCH` with an optional prerelease. */
|
|
10
|
+
export interface ICodexVersion {
|
|
11
|
+
major: number;
|
|
12
|
+
minor: number;
|
|
13
|
+
patch: number;
|
|
14
|
+
/** The prerelease after `-`, such as `alpha.2`, or null for a release. */
|
|
15
|
+
prerelease: string | null;
|
|
16
|
+
/** The exact text that was parsed. */
|
|
17
|
+
text: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** `unreadable`: not a Codex release number. `below_minimum`: older than the required release. */
|
|
21
|
+
export type TCodexVersionUnsupportedReason = 'unreadable' | 'below_minimum';
|
|
22
|
+
|
|
23
|
+
/** A Codex whose version cannot be read, or is older than the release a consumer requires. */
|
|
24
|
+
export class CodexVersionUnsupportedError extends Error {
|
|
25
|
+
public readonly reason: TCodexVersionUnsupportedReason;
|
|
26
|
+
/** The version text that was checked; null when none was reported. */
|
|
27
|
+
public readonly version: string | null;
|
|
28
|
+
public readonly minimum: string;
|
|
29
|
+
|
|
30
|
+
constructor(reasonArg: TCodexVersionUnsupportedReason, versionArg: string | null, minimumArg: string) {
|
|
31
|
+
super(reasonArg === 'unreadable'
|
|
32
|
+
? `Codex did not report a readable version; Codex ${minimumArg} or newer is required`
|
|
33
|
+
: `Codex ${versionArg} is older than the required Codex ${minimumArg}`);
|
|
34
|
+
this.name = 'CodexVersionUnsupportedError';
|
|
35
|
+
this.reason = reasonArg;
|
|
36
|
+
this.version = versionArg;
|
|
37
|
+
this.minimum = minimumArg;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const maximumVersionLength = 64;
|
|
42
|
+
const numericPart = '(0|[1-9]\\d*)';
|
|
43
|
+
const prereleasePart = '(?:0|[1-9]\\d*|\\d*[A-Za-z-][0-9A-Za-z-]*)';
|
|
44
|
+
const versionPattern = new RegExp(
|
|
45
|
+
`^${numericPart}\\.${numericPart}\\.${numericPart}(?:-(${prereleasePart}(?:\\.${prereleasePart})*))?$`,
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Parses exactly one Codex release number, such as `0.157.1` or `0.158.0-alpha.2`. Anything else
|
|
50
|
+
* returns null: a prefix such as `codex-cli `, surrounding whitespace, build metadata, leading
|
|
51
|
+
* zeros or components beyond the safe integer range.
|
|
52
|
+
*/
|
|
53
|
+
export const parseCodexVersion = (textArg: string): ICodexVersion | null => {
|
|
54
|
+
if (typeof textArg !== 'string' || textArg.length > maximumVersionLength) return null;
|
|
55
|
+
const match = versionPattern.exec(textArg);
|
|
56
|
+
if (!match) return null;
|
|
57
|
+
const [major, minor, patch] = [match[1], match[2], match[3]].map(Number);
|
|
58
|
+
if (![major, minor, patch].every(Number.isSafeInteger)) return null;
|
|
59
|
+
return { major, minor, patch, prerelease: match[4] ?? null, text: textArg };
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/** Orders by release number; a prerelease precedes the release of the same number. */
|
|
63
|
+
const isBelow = (versionArg: ICodexVersion, minimumArg: ICodexVersion): boolean => {
|
|
64
|
+
for (const key of ['major', 'minor', 'patch'] as const) {
|
|
65
|
+
if (versionArg[key] !== minimumArg[key]) return versionArg[key] < minimumArg[key];
|
|
66
|
+
}
|
|
67
|
+
return versionArg.prerelease !== null;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Returns the parsed version when it is `minimumArg` or newer, and throws
|
|
72
|
+
* `CodexVersionUnsupportedError` otherwise. A prerelease of a newer release is accepted; a
|
|
73
|
+
* prerelease of the minimum itself is older than the minimum. The minimum must be a release.
|
|
74
|
+
*/
|
|
75
|
+
export const requireCodexVersion = (
|
|
76
|
+
versionArg: string | null | undefined,
|
|
77
|
+
minimumArg: string = CODEX_UNIX_SOCKET_MINIMUM_VERSION,
|
|
78
|
+
): ICodexVersion => {
|
|
79
|
+
const minimum = parseCodexVersion(minimumArg);
|
|
80
|
+
if (!minimum || minimum.prerelease !== null) {
|
|
81
|
+
throw new Error('Codex minimum version must be a release number such as 0.156.0');
|
|
82
|
+
}
|
|
83
|
+
const reported = typeof versionArg === 'string' ? versionArg : null;
|
|
84
|
+
const version = reported === null ? null : parseCodexVersion(reported);
|
|
85
|
+
if (!version) throw new CodexVersionUnsupportedError('unreadable', reported, minimumArg);
|
|
86
|
+
if (isBelow(version, minimum)) throw new CodexVersionUnsupportedError('below_minimum', reported, minimumArg);
|
|
87
|
+
return version;
|
|
88
|
+
};
|
package/ts/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export * from './interfaces.js';
|
|
2
2
|
export * from './classes.codexappserverclient.js';
|
|
3
3
|
export * from './functions.codexunixsocket.js';
|
|
4
|
+
export * from './functions.codexversion.js';
|
|
4
5
|
export * from './classes.connectionregistry.js';
|
|
5
6
|
export * from './classes.dispatchregistry.js';
|
|
6
7
|
export * from './classes.mcptoolregistrar.js';
|