@brunobracaioli/aibrain 0.3.0-beta.18 → 0.4.0-beta.1
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 +27 -11
- package/dist/assets/claude/hooks/context-compaction.ts +1 -1
- package/dist/claude-compatibility.mjs +1 -1
- package/dist/claude-observation.mjs +1 -1
- package/dist/cli.mjs +18 -4
- package/dist/codex-compatibility.mjs +1 -1
- package/dist/engineering.mjs +190 -0
- package/dist/launch.mjs +2 -2
- package/dist/package.json +1 -1
- package/dist/platform.mjs +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
|
-
#
|
|
1
|
+
# aisac
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The AISAC client for Claude Code and Codex on Linux/WSL with Node.js 22 or newer:
|
|
4
|
+
AI Brain memory, context management and, for AISAC Founding Cohort members,
|
|
5
|
+
AISAC Engineering.
|
|
6
|
+
|
|
7
|
+
The command is `aisac`. `aibrain` is an alias for the same program, so existing
|
|
8
|
+
scripts and habits keep working. The same files are published as `@aisac/cli`
|
|
9
|
+
and, for existing installs, `@brunobracaioli/aibrain`. Install only one of them:
|
|
10
|
+
both provide the `aisac` and `aibrain` commands. State stays in `~/.aibrain`.
|
|
11
|
+
|
|
12
|
+
`aisac setup` and `aisac doctor` report the AISAC Engineering license
|
|
13
|
+
(`active`, `inactive` or `unavailable`). The server decides; the client only
|
|
14
|
+
shows the answer and sends the request only to the configured AI Brain address.
|
|
15
|
+
`aisac status` stays offline.
|
|
4
16
|
|
|
5
17
|
Codex capture also requires the `flock` utility from `util-linux`, normally
|
|
6
18
|
included in Linux/WSL. The launcher and doctor check availability before a
|
|
@@ -34,13 +46,17 @@ connection. Its failure or timeout closes only that auxiliary connection.
|
|
|
34
46
|
|
|
35
47
|
```sh
|
|
36
48
|
# Install the published beta:
|
|
37
|
-
npm install -g @
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
49
|
+
npm install -g @aisac/cli@beta
|
|
50
|
+
aisac setup --client both
|
|
51
|
+
aisac doctor --json
|
|
52
|
+
aisac claude
|
|
53
|
+
aisac codex
|
|
42
54
|
```
|
|
43
55
|
|
|
56
|
+
To move from `@brunobracaioli/aibrain` to `@aisac/cli`, close running sessions,
|
|
57
|
+
then run `npm uninstall -g @brunobracaioli/aibrain` before
|
|
58
|
+
`npm install -g @aisac/cli@beta`. Setup, notes and checkpoints are kept.
|
|
59
|
+
|
|
44
60
|
Installation does not contact AI Brain, change native client configuration, or
|
|
45
61
|
enable all paid accounts. Setup requires an existing AI Brain PAT, explicit
|
|
46
62
|
versioned disclosure, and successful provider-free pilot access. Claude and
|
|
@@ -53,10 +69,10 @@ explicitly acknowledge the displayed disclosure with
|
|
|
53
69
|
`aibrain setup --client both --token-file /private/path/brain.pat
|
|
54
70
|
--accept-disclosure`.
|
|
55
71
|
|
|
56
|
-
For a CI-reviewed tarball, use
|
|
57
|
-
`
|
|
58
|
-
`
|
|
59
|
-
|
|
72
|
+
For a CI-reviewed tarball, use `aisac-cli-*.tgz` with
|
|
73
|
+
`npm install -g ./aisac-cli-*.tgz` (the same files are also packed as
|
|
74
|
+
`brunobracaioli-aibrain-*.tgz`); registry installation is gated on the
|
|
75
|
+
operator's npm review and authentication.
|
|
60
76
|
|
|
61
77
|
For users of an earlier unscoped review tarball, close its client sessions,
|
|
62
78
|
run `npm uninstall -g aibrain`, and install this scoped package. Local
|
|
@@ -151,7 +151,7 @@ type Register = (
|
|
|
151
151
|
) => void;
|
|
152
152
|
|
|
153
153
|
const MAX_RESULT_BYTES = 256 * 1024;
|
|
154
|
-
const CLAUDE_HOOK_VERSION = '0.
|
|
154
|
+
const CLAUDE_HOOK_VERSION = '0.4.0-beta.1';
|
|
155
155
|
const MAX_DIAGNOSTIC_BYTE_COUNT = 128 * 1024 * 1024;
|
|
156
156
|
const COOLDOWN_MS = 60_000;
|
|
157
157
|
const MAX_TRUNCATED_CODE_POINTS = 300;
|
|
@@ -494,7 +494,7 @@ export async function resolveClaudeCompatibility(executable, packageRoot) {
|
|
|
494
494
|
/** @param {ClaudeCompatibility} decision */
|
|
495
495
|
export function claudeCompatibilityError(decision) {
|
|
496
496
|
const detected = decision.version ?? 'unavailable';
|
|
497
|
-
return `claude_version_unverified: Claude Code detected version ${detected}; reason ${decision.reason}; run
|
|
497
|
+
return `claude_version_unverified: Claude Code detected version ${detected}; reason ${decision.reason}; run aisac doctor --json or install a compatible stable native version.`;
|
|
498
498
|
}
|
|
499
499
|
|
|
500
500
|
export const claudeCompatibilityInternals = Object.freeze({
|
|
@@ -16,7 +16,7 @@ export const MAX_LOG_FILES = 3;
|
|
|
16
16
|
const LOCK_WAIT_MS = 250;
|
|
17
17
|
const LOCK_STALE_MS = 30_000;
|
|
18
18
|
const ADMISSION_MS = 60 * 60 * 1000;
|
|
19
|
-
export const ADAPTER_VERSION = '0.
|
|
19
|
+
export const ADAPTER_VERSION = '0.4.0-beta.1';
|
|
20
20
|
const MAX_DIAGNOSTIC_COUNT = 10_000_000;
|
|
21
21
|
const MAX_DIAGNOSTIC_BYTES = 128 * 1024 * 1024;
|
|
22
22
|
const CHECKPOINT_BACKENDS = new Set(['native_store', 'private_file']);
|
package/dist/cli.mjs
CHANGED
|
@@ -28,15 +28,18 @@ import { providerFreeProbe } from './mcp.mjs';
|
|
|
28
28
|
import { codexPolicyJson, makePolicy, publicPolicy, isProfile } from './policy.mjs';
|
|
29
29
|
import { readDiagnostics } from './claude-observation.mjs';
|
|
30
30
|
import { readCodexDiagnostics } from './codex-diagnostics.mjs';
|
|
31
|
+
import { checkEngineeringLicense, licenseLine, unavailableLicense } from './engineering.mjs';
|
|
31
32
|
|
|
32
|
-
const VERSION = '0.
|
|
33
|
+
const VERSION = '0.4.0-beta.1';
|
|
34
|
+
// The command is `aisac`; `aibrain` is an alias installed from the same entry.
|
|
35
|
+
const COMMAND = 'aisac';
|
|
33
36
|
const ENDPOINT = 'https://mcp.ai-brain.aisac.cloud/mcp';
|
|
34
37
|
const PACKAGE_ROOT = dirname(fileURLToPath(import.meta.url));
|
|
35
38
|
const LEGACY_DISCLOSURE_VERSION = 'aibrain-paid-context-v1-2026-10-09';
|
|
36
39
|
|
|
37
40
|
function usage() {
|
|
38
41
|
console.log(
|
|
39
|
-
|
|
42
|
+
`${COMMAND} ${VERSION}\n\nUsage:\n ${COMMAND} setup [--client claude|codex|both] [--context-profile upstream|conservative]\n [--compact-at 1..95] [--keep-threshold 0..1] [--preserve-recent 1..100]\n [--min-reduction 0..1] [--truncate-head-chars 0..4096]\n [--max-state-tokens 1000..25000] [--max-request-tokens 1000..30000]\n [--tool-policy compatible|read_only] [--goal TEXT]\n ${COMMAND} doctor [--json]\n ${COMMAND} status [--json]\n ${COMMAND} disable\n ${COMMAND} setup --undo\n ${COMMAND} claude [-- native-args...]\n ${COMMAND} codex [-- native-args...]\n\nPolicy notes:\n Automatic monitoring asks for compaction at compact-at percent; state/request\n limits are estimated upper bounds and do not override server quotas.\n min-reduction, truncate-head-chars and tool-policy apply only to Claude;\n Codex keeps native compaction and same-session recovery.`,
|
|
40
43
|
);
|
|
41
44
|
console.log(
|
|
42
45
|
'Telemetry: setup --telemetry on|off (omitted preserves the stored choice; fresh defaults off).',
|
|
@@ -44,11 +47,14 @@ function usage() {
|
|
|
44
47
|
console.log(
|
|
45
48
|
'Claude shadow: setup --claude-shadow on|off and --claude-diagnostics on|off (preview stays applied=false).',
|
|
46
49
|
);
|
|
50
|
+
console.log(
|
|
51
|
+
'AISAC Engineering: setup and doctor report the license; status stays offline. `aibrain` is an alias of `aisac`.',
|
|
52
|
+
);
|
|
47
53
|
}
|
|
48
54
|
|
|
49
55
|
// Telemetry is opt-in; setup omits the flag to preserve the stored choice.
|
|
50
56
|
function version() {
|
|
51
|
-
console.log(
|
|
57
|
+
console.log(`${COMMAND} ${VERSION}`);
|
|
52
58
|
}
|
|
53
59
|
|
|
54
60
|
async function maskedPrompt() {
|
|
@@ -495,6 +501,9 @@ async function setup(args) {
|
|
|
495
501
|
console.log(
|
|
496
502
|
`Configured ${clients} in ${root}. Provider-free access verified; no provider batch was used.`,
|
|
497
503
|
);
|
|
504
|
+
// Display only: the server decides again on every pack request (ADR-0047).
|
|
505
|
+
const line = licenseLine(await checkEngineeringLicense(endpoint, token));
|
|
506
|
+
if (line !== null) console.log(line);
|
|
498
507
|
}
|
|
499
508
|
|
|
500
509
|
/** @param {boolean} json */
|
|
@@ -534,6 +543,10 @@ async function doctor(json) {
|
|
|
534
543
|
...rawNativeVersions,
|
|
535
544
|
codex: codexCompatibility?.version ?? rawNativeVersions.codex,
|
|
536
545
|
};
|
|
546
|
+
// Optional capability: its state never changes doctor's exit code.
|
|
547
|
+
const engineering = config
|
|
548
|
+
? await checkEngineeringLicense(config.endpoint, config.token)
|
|
549
|
+
: unavailableLicense('not_configured');
|
|
537
550
|
const needsClaude = config?.clientSelection === 'claude' || config?.clientSelection === 'both';
|
|
538
551
|
const needsCodex = config?.clientSelection === 'codex' || config?.clientSelection === 'both';
|
|
539
552
|
const captureLockPlatform = needsCodex ? captureLockPlatformCheck() : null;
|
|
@@ -585,6 +598,7 @@ async function doctor(json) {
|
|
|
585
598
|
config?.claudeObservation?.disclosedAt !== null &&
|
|
586
599
|
config?.claudeObservation?.disclosedAt !== undefined,
|
|
587
600
|
},
|
|
601
|
+
engineering,
|
|
588
602
|
paidOrPilot: probe?.ok
|
|
589
603
|
? 'provider_free_probe_ok; paid entitlement or pilot invitation remains server-enforced'
|
|
590
604
|
: config
|
|
@@ -716,7 +730,7 @@ main(cliArgs).catch((error) => {
|
|
|
716
730
|
}),
|
|
717
731
|
);
|
|
718
732
|
} else {
|
|
719
|
-
console.error(
|
|
733
|
+
console.error(`${COMMAND}: ${errorMessage(error)}`);
|
|
720
734
|
}
|
|
721
735
|
process.exitCode = 1;
|
|
722
736
|
});
|
|
@@ -477,5 +477,5 @@ export async function resolveCodexCompatibility(executable, baseEnvironment = pr
|
|
|
477
477
|
|
|
478
478
|
/** @param {{reason:string, version:string|null}} decision @returns {string} */
|
|
479
479
|
export function codexCompatibilityError(decision) {
|
|
480
|
-
return `codex_protocol_incompatible:${decision.reason}:detected=${decision.version ?? 'unavailable'};run
|
|
480
|
+
return `codex_protocol_incompatible:${decision.reason}:detected=${decision.version ?? 'unavailable'};run aisac doctor --json`;
|
|
481
481
|
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* AISAC Engineering license check (ADR-0047 wave E3).
|
|
5
|
+
*
|
|
6
|
+
* The server is the only authority. This answer is shown to the user and
|
|
7
|
+
* never unlocks anything locally: every failure is reported as
|
|
8
|
+
* `unavailable`, never as licensed. The account token goes only to the
|
|
9
|
+
* configured AI Brain origin, redirects are refused, and the response is
|
|
10
|
+
* size-bounded and validated before any field is displayed.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const LICENSE_PATH = '/engineering/v1/license';
|
|
14
|
+
export const LICENSE_TIMEOUT_MS = 5_000;
|
|
15
|
+
const MAX_LICENSE_BODY_BYTES = 16 * 1024;
|
|
16
|
+
const MAX_URL_LENGTH = 2_048;
|
|
17
|
+
const ISO_INSTANT = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?Z$/u;
|
|
18
|
+
// Reasons are displayed, so only a plain identifier is accepted. New server
|
|
19
|
+
// reasons stay readable by old clients without an allowlist update.
|
|
20
|
+
const SAFE_REASON = /^[a-z][a-z0-9_]{0,63}$/u;
|
|
21
|
+
/** @type {ReadonlyMap<number, string>} */
|
|
22
|
+
const UNAVAILABLE_BY_STATUS = new Map([
|
|
23
|
+
[401, 'unauthorized'],
|
|
24
|
+
[404, 'not_offered'],
|
|
25
|
+
[429, 'rate_limited'],
|
|
26
|
+
[503, 'license_unavailable'],
|
|
27
|
+
]);
|
|
28
|
+
const SERVER_ERROR_STATUS = 500;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @typedef {{
|
|
32
|
+
* state: 'active' | 'inactive' | 'unavailable',
|
|
33
|
+
* reason: string,
|
|
34
|
+
* validUntil: string | null,
|
|
35
|
+
* purchaseUrl: string | null
|
|
36
|
+
* }} EngineeringLicense
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
/** @param {string} reason @returns {EngineeringLicense} */
|
|
40
|
+
export function unavailableLicense(reason) {
|
|
41
|
+
return { state: 'unavailable', reason, validUntil: null, purchaseUrl: null };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** @param {string} endpoint */
|
|
45
|
+
export function engineeringLicenseUrl(endpoint) {
|
|
46
|
+
return new URL(LICENSE_PATH, endpoint).toString();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** @param {unknown} value @returns {value is Record<string, unknown>} */
|
|
50
|
+
function isRecord(value) {
|
|
51
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** @param {unknown} value */
|
|
55
|
+
function isPurchaseUrl(value) {
|
|
56
|
+
if (typeof value !== 'string' || value.length > MAX_URL_LENGTH) return false;
|
|
57
|
+
try {
|
|
58
|
+
const url = new URL(value);
|
|
59
|
+
return url.protocol === 'https:' && url.username === '' && url.password === '';
|
|
60
|
+
} catch {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** @param {unknown} value */
|
|
66
|
+
function isValidUntil(value) {
|
|
67
|
+
return (
|
|
68
|
+
value === null ||
|
|
69
|
+
(typeof value === 'string' && ISO_INSTANT.test(value) && Number.isFinite(Date.parse(value)))
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Validate the 200 body of the license route (OpenAPI `EngineeringLicense`).
|
|
75
|
+
* Unknown extra fields are ignored; anything malformed rejects the whole body.
|
|
76
|
+
* @param {string} text
|
|
77
|
+
* @returns {EngineeringLicense | null}
|
|
78
|
+
*/
|
|
79
|
+
export function parseLicenseBody(text) {
|
|
80
|
+
let body;
|
|
81
|
+
try {
|
|
82
|
+
body = JSON.parse(text);
|
|
83
|
+
} catch {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
if (!isRecord(body) || body.schema_version !== 1 || body.capability !== 'engineering')
|
|
87
|
+
return null;
|
|
88
|
+
const { state, reason, valid_until: validUntil, purchase_url: purchaseUrl } = body;
|
|
89
|
+
if (state !== 'active' && state !== 'inactive') return null;
|
|
90
|
+
if (typeof reason !== 'string' || !SAFE_REASON.test(reason)) return null;
|
|
91
|
+
if (!isValidUntil(validUntil) || !isPurchaseUrl(purchaseUrl)) return null;
|
|
92
|
+
return {
|
|
93
|
+
state,
|
|
94
|
+
reason,
|
|
95
|
+
validUntil: /** @type {string | null} */ (validUntil),
|
|
96
|
+
purchaseUrl: /** @type {string} */ (purchaseUrl),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** @param {Response} response */
|
|
101
|
+
async function boundedText(response) {
|
|
102
|
+
const declared = Number(response.headers.get('content-length') ?? '0');
|
|
103
|
+
if (declared > MAX_LICENSE_BODY_BYTES) throw new Error('license_body_too_large');
|
|
104
|
+
if (response.body === null) return '';
|
|
105
|
+
const reader = response.body.getReader();
|
|
106
|
+
/** @type {Uint8Array[]} */
|
|
107
|
+
const chunks = [];
|
|
108
|
+
let size = 0;
|
|
109
|
+
for (;;) {
|
|
110
|
+
const { done, value } = await reader.read();
|
|
111
|
+
if (done) break;
|
|
112
|
+
size += value.byteLength;
|
|
113
|
+
if (size > MAX_LICENSE_BODY_BYTES) {
|
|
114
|
+
await reader.cancel().catch(() => undefined);
|
|
115
|
+
throw new Error('license_body_too_large');
|
|
116
|
+
}
|
|
117
|
+
chunks.push(value);
|
|
118
|
+
}
|
|
119
|
+
return Buffer.concat(chunks).toString('utf8');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** @param {unknown} error */
|
|
123
|
+
function isTimeout(error) {
|
|
124
|
+
return error instanceof Error && (error.name === 'TimeoutError' || error.name === 'AbortError');
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** @param {number} status */
|
|
128
|
+
function unavailableReasonFor(status) {
|
|
129
|
+
return (
|
|
130
|
+
UNAVAILABLE_BY_STATUS.get(status) ??
|
|
131
|
+
(status >= SERVER_ERROR_STATUS ? 'server_error' : 'unexpected_status')
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Ask the server whether this account holds the AISAC Engineering license.
|
|
137
|
+
* @param {string} endpoint configured AI Brain MCP endpoint (already validated at setup)
|
|
138
|
+
* @param {string} token account PAT
|
|
139
|
+
* @param {{ fetchImpl?: typeof fetch, timeoutMs?: number }} [options]
|
|
140
|
+
* @returns {Promise<EngineeringLicense>}
|
|
141
|
+
*/
|
|
142
|
+
export async function checkEngineeringLicense(endpoint, token, options = {}) {
|
|
143
|
+
const { fetchImpl = fetch, timeoutMs = LICENSE_TIMEOUT_MS } = options;
|
|
144
|
+
let url;
|
|
145
|
+
try {
|
|
146
|
+
url = engineeringLicenseUrl(endpoint);
|
|
147
|
+
} catch {
|
|
148
|
+
return unavailableLicense('invalid_endpoint');
|
|
149
|
+
}
|
|
150
|
+
const signal = AbortSignal.timeout(timeoutMs);
|
|
151
|
+
let response;
|
|
152
|
+
try {
|
|
153
|
+
response = await fetchImpl(url, {
|
|
154
|
+
method: 'GET',
|
|
155
|
+
redirect: 'error',
|
|
156
|
+
signal,
|
|
157
|
+
headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' },
|
|
158
|
+
});
|
|
159
|
+
} catch (error) {
|
|
160
|
+
return unavailableLicense(isTimeout(error) ? 'timeout' : 'unreachable');
|
|
161
|
+
}
|
|
162
|
+
if (response.status !== 200) {
|
|
163
|
+
await response.body?.cancel().catch(() => undefined);
|
|
164
|
+
return unavailableLicense(unavailableReasonFor(response.status));
|
|
165
|
+
}
|
|
166
|
+
let text;
|
|
167
|
+
try {
|
|
168
|
+
text = await boundedText(response);
|
|
169
|
+
} catch (error) {
|
|
170
|
+
return unavailableLicense(isTimeout(error) ? 'timeout' : 'invalid_response');
|
|
171
|
+
}
|
|
172
|
+
return parseLicenseBody(text) ?? unavailableLicense('invalid_response');
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* One line for `aisac setup`. Returns null while the server does not offer the
|
|
177
|
+
* capability yet, so memory-only users see no new noise.
|
|
178
|
+
* @param {EngineeringLicense} license
|
|
179
|
+
* @returns {string | null}
|
|
180
|
+
*/
|
|
181
|
+
export function licenseLine(license) {
|
|
182
|
+
if (license.state === 'active') {
|
|
183
|
+
const until = license.validUntil === null ? '' : ` until ${license.validUntil.slice(0, 10)}`;
|
|
184
|
+
return `AISAC Engineering: active${until}.`;
|
|
185
|
+
}
|
|
186
|
+
if (license.state === 'inactive')
|
|
187
|
+
return `AISAC Engineering: inactive (${license.reason}) — ${license.purchaseUrl ?? ''}`.trimEnd();
|
|
188
|
+
if (license.reason === 'not_offered') return null;
|
|
189
|
+
return `AISAC Engineering: unavailable (${license.reason}); memory and context are unaffected.`;
|
|
190
|
+
}
|
package/dist/launch.mjs
CHANGED
|
@@ -413,7 +413,7 @@ export async function launchClaude(config, root, args) {
|
|
|
413
413
|
const pinnedExecutable = compatibility.executable ?? executable;
|
|
414
414
|
if (compatibility.status === 'host_checked')
|
|
415
415
|
process.stderr.write(
|
|
416
|
-
`
|
|
416
|
+
`aisac: Claude ${compatibility.version ?? 'unknown'} accepted by the local native host contract; end-to-end recovery remains separately qualified.\n`,
|
|
417
417
|
);
|
|
418
418
|
const currentVersion = compatibility.version ?? nativeVersion(pinnedExecutable);
|
|
419
419
|
const pluginRoot = await assertClaudeAssets(validated.packageRoot);
|
|
@@ -575,7 +575,7 @@ export async function launchCodex(config, root, args) {
|
|
|
575
575
|
if (compatibility.version === null) throw new Error('codex_version_unverified');
|
|
576
576
|
if (compatibility.status === 'protocol_match')
|
|
577
577
|
process.stderr.write(
|
|
578
|
-
`
|
|
578
|
+
`aisac: Codex ${compatibility.version} matched the local protocol contract; native behavioral qualification is not established.\n`,
|
|
579
579
|
);
|
|
580
580
|
const launchPlan = await parseCodexLaunchArgs(args, process.cwd());
|
|
581
581
|
const packageRoot = resolve(validated.packageRoot);
|
package/dist/package.json
CHANGED
package/dist/platform.mjs
CHANGED
|
@@ -116,7 +116,7 @@ export function nativeVersionDiagnostic(client, version) {
|
|
|
116
116
|
: 'unavailable';
|
|
117
117
|
const label = client === 'claude' ? 'Claude Code' : 'Codex CLI';
|
|
118
118
|
const tested = TESTED_NATIVE_VERSIONS[client].join(', ');
|
|
119
|
-
return `${client}_version_unverified: ${label} detected version ${safeDetected}; tested stable versions: ${tested}. Update AI Brain to a compatible version, then run
|
|
119
|
+
return `${client}_version_unverified: ${label} detected version ${safeDetected}; tested stable versions: ${tested}. Update AI Brain to a compatible version, then run aisac doctor --json or install a qualified stable native version.`;
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
/** @param {'claude'|'codex'} client @param {string} version */
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brunobracaioli/aibrain",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0-beta.1",
|
|
4
4
|
"private": false,
|
|
5
|
-
"description": "
|
|
5
|
+
"description": "AISAC client for Claude Code and Codex: AI Brain memory, context management and AISAC Engineering",
|
|
6
6
|
"license": "UNLICENSED",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"bin": {
|
|
9
|
+
"aisac": "./dist/cli.mjs",
|
|
9
10
|
"aibrain": "./dist/cli.mjs"
|
|
10
11
|
},
|
|
11
12
|
"files": [
|