mason-context 0.13.0 → 0.14.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/CHANGELOG.md +12 -1
- package/README.md +39 -546
- package/dist/mason-auto.js +185 -52
- package/dist/mason-auto.js.map +1 -1
- package/dist/mason-mcp.js +351 -218
- package/dist/mason-mcp.js.map +1 -1
- package/dist/mason.js +4842 -15
- package/dist/mason.js.map +1 -1
- package/package.json +4 -2
package/dist/mason-auto.js
CHANGED
|
@@ -2398,7 +2398,7 @@ var init_evidence = __esm({
|
|
|
2398
2398
|
init_dead_command();
|
|
2399
2399
|
init_storage();
|
|
2400
2400
|
exec7 = promisify7(execFile7);
|
|
2401
|
-
engineVersion = true ? "0.
|
|
2401
|
+
engineVersion = true ? "0.14.0" : "development";
|
|
2402
2402
|
hash = (value) => createHash3("sha256").update(JSON.stringify(value)).digest("hex");
|
|
2403
2403
|
internal = (file) => file === ".mason" || file === ".mason/reports" || file.startsWith(".mason/reports/");
|
|
2404
2404
|
cacheSchema = z5.object({ version: z5.literal(1), entries: z5.record(z5.object({ key: z5.string(), result: checkResultSchema })), digest: z5.string() });
|
|
@@ -2747,7 +2747,8 @@ var init_model = __esm({
|
|
|
2747
2747
|
runtimeSchema = z7.object({
|
|
2748
2748
|
id: z7.string().regex(/^[a-f0-9]{24}$/),
|
|
2749
2749
|
version: z7.string().regex(/^\d+\.\d+\.\d+(?:-[A-Za-z0-9.-]+)?$/),
|
|
2750
|
-
hashes: z7.record(z7.string().regex(/^[a-f0-9]{64}$/))
|
|
2750
|
+
hashes: z7.record(z7.string().regex(/^[a-f0-9]{64}$/)),
|
|
2751
|
+
bundle: z7.object({ target: z7.string(), manifestHash: z7.string().regex(/^[a-f0-9]{64}$/) }).optional()
|
|
2751
2752
|
});
|
|
2752
2753
|
setupHostSchema = z7.object({
|
|
2753
2754
|
runtime: runtimeSchema,
|
|
@@ -3905,18 +3906,55 @@ var init_files2 = __esm({
|
|
|
3905
3906
|
});
|
|
3906
3907
|
|
|
3907
3908
|
// src/setup/launcher.ts
|
|
3908
|
-
var BOOTSTRAP, mcpCommand, hookCommand, LAUNCHER;
|
|
3909
|
+
var BOOTSTRAP, POWERSHELL_BOOTSTRAP, mcpCommand, hookCommand, SHELL_LAUNCHER, POWERSHELL_LAUNCHER, LAUNCHER;
|
|
3909
3910
|
var init_launcher = __esm({
|
|
3910
3911
|
"src/setup/launcher.ts"() {
|
|
3911
3912
|
"use strict";
|
|
3912
3913
|
BOOTSTRAP = "require(require('node:path').join(require('node:child_process').execFileSync('git',['rev-parse','--show-toplevel'],{encoding:'utf8'}).trim(),'.mason','run.cjs'))";
|
|
3913
|
-
|
|
3914
|
-
|
|
3914
|
+
POWERSHELL_BOOTSTRAP = "& (Join-Path (git rev-parse --show-toplevel) '.mason/run.ps1')";
|
|
3915
|
+
mcpCommand = (host, runtime) => !runtime?.bundle ? { command: "node", args: ["-e", BOOTSTRAP, "--", host, "mcp"] } : process.platform === "win32" ? { command: "powershell.exe", args: ["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", POWERSHELL_BOOTSTRAP, host, "mcp"] } : { command: "sh", args: ["-c", 'exec sh "$(git rev-parse --show-toplevel)/.mason/run.sh" "$@"', "mason", host, "mcp"] };
|
|
3916
|
+
hookCommand = (host, runtime) => !runtime?.bundle ? `node -e "${BOOTSTRAP}" -- ${host} auto` : process.platform === "win32" ? `powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -Command "${POWERSHELL_BOOTSTRAP} ${host} auto"` : `sh "$(git rev-parse --show-toplevel)/.mason/run.sh" ${host} auto`;
|
|
3917
|
+
SHELL_LAUNCHER = `#!/bin/sh
|
|
3918
|
+
set -eu
|
|
3919
|
+
advisory=0
|
|
3920
|
+
if [ "\${2-}" = auto ] && [ "\${3-}" = hook ]; then advisory=1; fi
|
|
3921
|
+
fail() {
|
|
3922
|
+
if [ "$advisory" -eq 1 ]; then printf '%s\\n' '{"systemMessage":"Mason runtime unavailable. Rerun mason setup for this checkout; verification was not established."}'; exit 0; fi
|
|
3923
|
+
echo 'Mason runtime unavailable. Rerun mason setup for this checkout.' >&2; exit 2
|
|
3924
|
+
}
|
|
3925
|
+
root=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) || fail
|
|
3926
|
+
case "\${1-}" in codex|claude) ;; *) fail ;; esac
|
|
3927
|
+
pointer="$root/.mason/runtime/$1.txt"
|
|
3928
|
+
[ -f "$pointer" ] || fail
|
|
3929
|
+
runtime=$(cat "$pointer") || fail
|
|
3930
|
+
case "$runtime" in ''|*[!0-9a-f]*) fail ;; esac
|
|
3931
|
+
[ "\${#runtime}" -eq 24 ] || fail
|
|
3932
|
+
[ -x "$root/.mason/runtime/$runtime/node" ] || fail
|
|
3933
|
+
if [ "$advisory" -eq 1 ]; then
|
|
3934
|
+
"$root/.mason/runtime/$runtime/node" "$root/.mason/run.cjs" "$@" || fail
|
|
3935
|
+
else
|
|
3936
|
+
exec "$root/.mason/runtime/$runtime/node" "$root/.mason/run.cjs" "$@"
|
|
3937
|
+
fi
|
|
3938
|
+
`;
|
|
3939
|
+
POWERSHELL_LAUNCHER = `# Managed by mason setup; no system Node installation is used.
|
|
3940
|
+
$ErrorActionPreference = 'Stop'
|
|
3941
|
+
$advisory = $args.Count -ge 3 -and $args[1] -eq 'auto' -and $args[2] -eq 'hook'
|
|
3942
|
+
try {
|
|
3943
|
+
if ($args.Count -lt 2 -or $args[0] -notin @('codex','claude')) { throw 'Invalid Mason host.' }
|
|
3944
|
+
$runtime = (Get-Content -LiteralPath (Join-Path $PSScriptRoot ('runtime/' + $args[0] + '.txt')) -Raw).Trim()
|
|
3945
|
+
if ($runtime -cnotmatch '^[a-f0-9]{24}$') { throw 'Invalid Mason runtime pointer.' }
|
|
3946
|
+
& (Join-Path $PSScriptRoot ('runtime/' + $runtime + '/node.exe')) (Join-Path $PSScriptRoot 'run.cjs') @args
|
|
3947
|
+
if ($LASTEXITCODE -ne 0) { throw 'Mason runtime failed.' }
|
|
3948
|
+
} catch {
|
|
3949
|
+
if ($advisory) { Write-Output '{"systemMessage":"Mason runtime unavailable. Rerun mason setup for this checkout; verification was not established."}'; exit 0 }
|
|
3950
|
+
[Console]::Error.WriteLine($_.Exception.Message); exit 2
|
|
3951
|
+
}
|
|
3952
|
+
`;
|
|
3915
3953
|
LAUNCHER = `// Managed by mason-auto setup. Rerun setup to install this checkout's pinned runtime.
|
|
3916
3954
|
const fs = require('node:fs');
|
|
3917
3955
|
const path = require('node:path');
|
|
3918
3956
|
const {pathToFileURL} = require('node:url');
|
|
3919
|
-
const launchArgs = process.argv.slice(1);
|
|
3957
|
+
const launchArgs = process.argv.slice(process.argv[1] && path.resolve(process.argv[1]) === __filename ? 2 : 1);
|
|
3920
3958
|
(async () => {
|
|
3921
3959
|
const root = path.dirname(__dirname);
|
|
3922
3960
|
const args = launchArgs;
|
|
@@ -3925,7 +3963,13 @@ const launchArgs = process.argv.slice(1);
|
|
|
3925
3963
|
const setup = JSON.parse(fs.readFileSync(path.join(__dirname, 'setup.json'), 'utf8'));
|
|
3926
3964
|
const entry = setup.hosts?.[host];
|
|
3927
3965
|
if (setup.version !== 1 || !entry || !/^[a-f0-9]{24}$/.test(entry.runtime?.id)) throw new Error('Mason is not configured for this host. Rerun mason-auto setup.');
|
|
3928
|
-
const
|
|
3966
|
+
const base = path.join(__dirname, 'runtime', entry.runtime.id);
|
|
3967
|
+
if (entry.runtime.bundle) {
|
|
3968
|
+
const node = process.platform === 'win32' ? 'node.exe' : 'node';
|
|
3969
|
+
if (fs.realpathSync(process.execPath) !== fs.realpathSync(path.join(base, node))) throw new Error('Mason runtime pointer differs from the configured version. Rerun setup.');
|
|
3970
|
+
}
|
|
3971
|
+
const binary = entry.runtime.bundle ? path.join(base, 'app', 'dist', 'mason-' + command + '.js')
|
|
3972
|
+
: path.join(base, 'node_modules', 'mason-context', 'dist', 'mason-' + command + '.js');
|
|
3929
3973
|
if (!fs.existsSync(binary)) throw new Error('The pinned Mason runtime is missing in this checkout. Rerun mason-auto setup --host ' + host + '.');
|
|
3930
3974
|
process.env.MASON_SETUP_ROOT = root;
|
|
3931
3975
|
process.env.MASON_SETUP_HOST = host;
|
|
@@ -3973,22 +4017,22 @@ async function instructionEdits(root, host) {
|
|
|
3973
4017
|
}
|
|
3974
4018
|
return edits;
|
|
3975
4019
|
}
|
|
3976
|
-
function managedMcp(previous, host) {
|
|
4020
|
+
function managedMcp(previous, host, runtime) {
|
|
3977
4021
|
const options = previous === void 0 ? {} : object.parse(previous);
|
|
3978
4022
|
for (const key of ["command", "args", "url", "type", "headers", "http_headers", "env_http_headers", "bearer_token_env_var"]) delete options[key];
|
|
3979
|
-
return { ...options, ...mcpCommand(host) };
|
|
4023
|
+
return { ...options, ...mcpCommand(host, runtime) };
|
|
3980
4024
|
}
|
|
3981
|
-
async function mcpEdit(root, host) {
|
|
4025
|
+
async function mcpEdit(root, host, runtime) {
|
|
3982
4026
|
const file = mcpPath(host), before = await readText(root, file);
|
|
3983
4027
|
if (host === "claude") {
|
|
3984
4028
|
const config2 = before === null ? {} : object.parse(JSON.parse(before));
|
|
3985
4029
|
const servers2 = object.parse(config2.mcpServers ?? {});
|
|
3986
|
-
return { path: file, before, after: JSON.stringify({ ...config2, mcpServers: { ...servers2, mason: managedMcp(servers2.mason, host) } }, null, 2) + "\n" };
|
|
4030
|
+
return { path: file, before, after: JSON.stringify({ ...config2, mcpServers: { ...servers2, mason: managedMcp(servers2.mason, host, runtime) } }, null, 2) + "\n" };
|
|
3987
4031
|
}
|
|
3988
4032
|
const config = parse(before ?? "");
|
|
3989
4033
|
const managed = (before ?? "").includes(TOML_START);
|
|
3990
4034
|
const servers = config.mcp_servers;
|
|
3991
|
-
const desired = managedMcp(servers?.mason, host);
|
|
4035
|
+
const desired = managedMcp(servers?.mason, host, runtime);
|
|
3992
4036
|
let base = before ?? "";
|
|
3993
4037
|
if (servers?.mason && !managed) {
|
|
3994
4038
|
const headers = [...base.matchAll(/^\s*\[\[?.+?\]\]?[^\S\r\n]*(?:#.*)?$/gm)];
|
|
@@ -4002,10 +4046,10 @@ async function mcpEdit(root, host) {
|
|
|
4002
4046
|
if (!isDeepStrictEqual(parsed, expected)) throw new Error("Could not safely configure the Mason MCP entry without changing other settings.");
|
|
4003
4047
|
return { path: file, before, after };
|
|
4004
4048
|
}
|
|
4005
|
-
async function hookEdits(root, host) {
|
|
4049
|
+
async function hookEdits(root, host, runtime) {
|
|
4006
4050
|
const file = configPath(host), before = await readText(root, file);
|
|
4007
4051
|
const { planAutomationInstall: planAutomationInstall2 } = await Promise.resolve().then(() => (init_install(), install_exports));
|
|
4008
|
-
const plan = await planAutomationInstall2(root, host, hookCommand(host));
|
|
4052
|
+
const plan = await planAutomationInstall2(root, host, hookCommand(host, runtime));
|
|
4009
4053
|
return [
|
|
4010
4054
|
{ path: file, before, after: JSON.stringify(plan.config, null, 2) + "\n" },
|
|
4011
4055
|
{ path: ".mason/automation.json", before: await readText(root, ".mason/automation.json"), after: JSON.stringify(plan.record, null, 2) + "\n" }
|
|
@@ -4021,18 +4065,20 @@ async function ancillaryEdits(root) {
|
|
|
4021
4065
|
if (error.code !== 1) throw error;
|
|
4022
4066
|
}
|
|
4023
4067
|
const retainParentRule = parentIgnored || !!ignore?.replace(/\r\n/g, "\n").includes("# mason:ignore:start\n!/.mason/\n/.mason/*");
|
|
4024
|
-
const rules = [...retainParentRule ? ["!/.mason/", "/.mason/*"] : [], "!/.mason/decisions/", "!/.mason/decisions/**", "!/.mason/setup.json", "!/.mason/automation.json", "!/.mason/project.json", "!/.mason/run.cjs", "/.mason/reports/", "/.mason/runtime/"].join("\n");
|
|
4068
|
+
const rules = [...retainParentRule ? ["!/.mason/", "/.mason/*"] : [], "!/.mason/decisions/", "!/.mason/decisions/**", "!/.mason/setup.json", "!/.mason/automation.json", "!/.mason/project.json", "!/.mason/run.cjs", "!/.mason/run.sh", "!/.mason/run.ps1", "/.mason/reports/", "/.mason/runtime/"].join("\n");
|
|
4025
4069
|
return [
|
|
4026
4070
|
{ path: ".gitignore", before: ignore, after: managedBlock(ignore ?? "", "# mason:ignore:start", "# mason:ignore:end", rules) },
|
|
4027
|
-
{ path: ".mason/run.cjs", before: await readText(root, ".mason/run.cjs"), after: LAUNCHER }
|
|
4071
|
+
{ path: ".mason/run.cjs", before: await readText(root, ".mason/run.cjs"), after: LAUNCHER },
|
|
4072
|
+
{ path: ".mason/run.sh", before: await readText(root, ".mason/run.sh"), after: SHELL_LAUNCHER },
|
|
4073
|
+
{ path: ".mason/run.ps1", before: await readText(root, ".mason/run.ps1"), after: POWERSHELL_LAUNCHER }
|
|
4028
4074
|
];
|
|
4029
4075
|
}
|
|
4030
|
-
async function inspectHostConfig(root, host, plannedMcp) {
|
|
4076
|
+
async function inspectHostConfig(root, host, plannedMcp, runtime) {
|
|
4031
4077
|
const text2 = plannedMcp ?? await readText(root, mcpPath(host));
|
|
4032
4078
|
const config = host === "codex" ? parse(text2 ?? "") : object.parse(JSON.parse(text2 ?? "{}"));
|
|
4033
4079
|
const servers = host === "codex" ? config.mcp_servers : config.mcpServers;
|
|
4034
4080
|
const hooks = automationConfigSchema.parse(JSON.parse(await readText(root, configPath(host)) ?? "{}"));
|
|
4035
|
-
const expected = hookConfig(host, hookCommand(host));
|
|
4081
|
+
const expected = hookConfig(host, hookCommand(host, runtime));
|
|
4036
4082
|
const mcp = servers?.mason === void 0 ? null : object.parse(servers.mason);
|
|
4037
4083
|
return {
|
|
4038
4084
|
mcp,
|
|
@@ -4063,40 +4109,109 @@ var init_config = __esm({
|
|
|
4063
4109
|
}
|
|
4064
4110
|
});
|
|
4065
4111
|
|
|
4066
|
-
// src/
|
|
4112
|
+
// src/distribution/bundle.ts
|
|
4067
4113
|
import fs16 from "fs/promises";
|
|
4068
4114
|
import path21 from "path";
|
|
4115
|
+
import { createHash as createHash4 } from "crypto";
|
|
4116
|
+
import { z as z16 } from "zod";
|
|
4117
|
+
async function readBundle(root) {
|
|
4118
|
+
const bytes = await fs16.readFile(await storePath(root, "bundle.json"));
|
|
4119
|
+
const manifest = bundleSchema.parse(JSON.parse(bytes.toString("utf8")));
|
|
4120
|
+
const windows = manifest.target.startsWith("win32-");
|
|
4121
|
+
for (const file of Object.keys(manifest.files)) {
|
|
4122
|
+
if (file.includes("\\") || file.split("/").some((p) => !p || p === "." || p === "..") || path21.isAbsolute(file)) {
|
|
4123
|
+
throw new Error("Unsafe path in Mason bundle: " + file);
|
|
4124
|
+
}
|
|
4125
|
+
}
|
|
4126
|
+
for (const required of [windows ? "node.exe" : "node", "app/package.json", "app/dist/mason.js", "app/dist/mason-auto.js", "app/dist/mason-mcp.js"]) {
|
|
4127
|
+
if (!manifest.files[required]) throw new Error("Incomplete Mason bundle: " + required);
|
|
4128
|
+
}
|
|
4129
|
+
return { manifest, manifestHash: sha256(bytes) };
|
|
4130
|
+
}
|
|
4131
|
+
async function verifyBundle(root, expectedHash) {
|
|
4132
|
+
const bundle = await readBundle(root);
|
|
4133
|
+
if (expectedHash && bundle.manifestHash !== expectedHash) throw new Error("Mason bundle manifest changed.");
|
|
4134
|
+
if (bundle.manifest.target !== `${process.platform}-${process.arch}`) throw new Error("Mason bundle is for another platform. Install this platform's bundle and rerun setup.");
|
|
4135
|
+
const files = Object.keys(bundle.manifest.files);
|
|
4136
|
+
for (let i = 0; i < files.length; i += 8) await Promise.all(files.slice(i, i + 8).map(async (file) => {
|
|
4137
|
+
if (sha256(await fs16.readFile(await storePath(root, file))) !== bundle.manifest.files[file]) throw new Error("Mason bundle checksum mismatch: " + file);
|
|
4138
|
+
}));
|
|
4139
|
+
return bundle;
|
|
4140
|
+
}
|
|
4141
|
+
var targets, digest2, bundleSchema, sha256;
|
|
4142
|
+
var init_bundle = __esm({
|
|
4143
|
+
"src/distribution/bundle.ts"() {
|
|
4144
|
+
"use strict";
|
|
4145
|
+
init_storage();
|
|
4146
|
+
targets = ["darwin-arm64", "darwin-x64", "linux-arm64", "linux-x64", "win32-arm64", "win32-x64"];
|
|
4147
|
+
digest2 = z16.string().regex(/^[a-f0-9]{64}$/);
|
|
4148
|
+
bundleSchema = z16.object({
|
|
4149
|
+
format: z16.literal(1),
|
|
4150
|
+
version: z16.string().regex(/^\d+\.\d+\.\d+(?:-[A-Za-z0-9.-]+)?$/),
|
|
4151
|
+
target: z16.enum(targets),
|
|
4152
|
+
nodeVersion: z16.string(),
|
|
4153
|
+
files: z16.record(digest2)
|
|
4154
|
+
}).strict();
|
|
4155
|
+
sha256 = (bytes) => createHash4("sha256").update(bytes).digest("hex");
|
|
4156
|
+
}
|
|
4157
|
+
});
|
|
4158
|
+
|
|
4159
|
+
// src/setup/runtime.ts
|
|
4160
|
+
import fs17 from "fs/promises";
|
|
4161
|
+
import path22 from "path";
|
|
4069
4162
|
import { fileURLToPath } from "url";
|
|
4070
|
-
import { randomUUID as randomUUID6, createHash as
|
|
4163
|
+
import { randomUUID as randomUUID6, createHash as createHash5 } from "crypto";
|
|
4071
4164
|
import { execFile as execFile10 } from "child_process";
|
|
4072
4165
|
import { promisify as promisify10 } from "util";
|
|
4073
4166
|
async function packageRoot() {
|
|
4074
|
-
let directory =
|
|
4167
|
+
let directory = path22.dirname(fileURLToPath(import.meta.url));
|
|
4075
4168
|
for (let i = 0; i < 8; i++) {
|
|
4076
4169
|
try {
|
|
4077
|
-
const pkg = JSON.parse(await
|
|
4170
|
+
const pkg = JSON.parse(await fs17.readFile(path22.join(directory, "package.json"), "utf8"));
|
|
4078
4171
|
if (pkg.name === "mason-context") return directory;
|
|
4079
4172
|
} catch {
|
|
4080
4173
|
}
|
|
4081
|
-
directory =
|
|
4174
|
+
directory = path22.dirname(directory);
|
|
4082
4175
|
}
|
|
4083
4176
|
throw new Error("Cannot locate the executing Mason distribution. Reinstall Mason and rerun setup.");
|
|
4084
4177
|
}
|
|
4085
4178
|
async function sourceRuntime() {
|
|
4086
4179
|
const source = await packageRoot();
|
|
4087
|
-
const pkg = JSON.parse(await
|
|
4088
|
-
const hashes = Object.fromEntries(await Promise.all(BINARIES.map(async (file) => [file, checksum(await
|
|
4180
|
+
const pkg = JSON.parse(await fs17.readFile(path22.join(source, "package.json"), "utf8"));
|
|
4181
|
+
const hashes = Object.fromEntries(await Promise.all(BINARIES.map(async (file) => [file, checksum(await fs17.readFile(path22.join(source, file)))])));
|
|
4182
|
+
const bundleRoot = path22.dirname(source);
|
|
4183
|
+
const bundleExists = await fs17.lstat(path22.join(bundleRoot, "bundle.json")).then(() => true, (error) => {
|
|
4184
|
+
if (error.code === "ENOENT") return false;
|
|
4185
|
+
throw error;
|
|
4186
|
+
});
|
|
4187
|
+
if (bundleExists) {
|
|
4188
|
+
const { manifest, manifestHash } = await verifyBundle(bundleRoot);
|
|
4189
|
+
if (manifest.version !== pkg.version) throw new Error("Mason package and bundle versions differ.");
|
|
4190
|
+
return { source, bundleRoot, runtime: runtimeSchema.parse({
|
|
4191
|
+
id: manifestHash.slice(0, 24),
|
|
4192
|
+
version: pkg.version,
|
|
4193
|
+
hashes,
|
|
4194
|
+
bundle: { target: manifest.target, manifestHash }
|
|
4195
|
+
}) };
|
|
4196
|
+
}
|
|
4089
4197
|
return { source, runtime: runtimeSchema.parse({ id: hash([pkg.version, pkg.dependencies, hashes]).slice(0, 24), version: pkg.version, hashes }) };
|
|
4090
4198
|
}
|
|
4091
4199
|
async function verifyRuntime(root, runtime) {
|
|
4092
4200
|
try {
|
|
4093
4201
|
const saved = await readStoreJson(root, `.mason/runtime/${runtime.id}/receipt.json`);
|
|
4094
4202
|
if (JSON.stringify(saved) !== JSON.stringify(runtime)) return false;
|
|
4203
|
+
if (runtime.bundle) {
|
|
4204
|
+
const base2 = await storePath(root, `.mason/runtime/${runtime.id}`);
|
|
4205
|
+
const { manifest } = await verifyBundle(base2, runtime.bundle.manifestHash);
|
|
4206
|
+
if (manifest.version !== runtime.version || manifest.target !== runtime.bundle.target) return false;
|
|
4207
|
+
for (const file of BINARIES) if (manifest.files["app/" + file] !== runtime.hashes[file]) return false;
|
|
4208
|
+
return true;
|
|
4209
|
+
}
|
|
4095
4210
|
const base = `.mason/runtime/${runtime.id}/node_modules/mason-context/`;
|
|
4096
4211
|
const pkg = await readStoreJson(root, base + "package.json");
|
|
4097
4212
|
if (pkg?.name !== "mason-context" || pkg.version !== runtime.version) return false;
|
|
4098
4213
|
for (const file of BINARIES) {
|
|
4099
|
-
if (checksum(await
|
|
4214
|
+
if (checksum(await fs17.readFile(await storePath(root, base + file))) !== runtime.hashes[file]) return false;
|
|
4100
4215
|
}
|
|
4101
4216
|
return true;
|
|
4102
4217
|
} catch {
|
|
@@ -4109,30 +4224,42 @@ async function installRuntime(root, selected) {
|
|
|
4109
4224
|
const relative = `.mason/runtime/${runtime.id}`;
|
|
4110
4225
|
const target = await storePath(root, relative, true);
|
|
4111
4226
|
const stage = await storePath(root, ".mason/runtime/.install-" + randomUUID6(), true);
|
|
4112
|
-
await
|
|
4227
|
+
await fs17.mkdir(stage);
|
|
4113
4228
|
try {
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
4117
|
-
|
|
4118
|
-
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
|
|
4229
|
+
if (runtime.bundle) {
|
|
4230
|
+
if (!selected.bundleRoot) throw new Error("Standalone bundle source is missing.");
|
|
4231
|
+
const { manifest } = await verifyBundle(selected.bundleRoot, runtime.bundle.manifestHash);
|
|
4232
|
+
for (const file of ["bundle.json", ...Object.keys(manifest.files)]) {
|
|
4233
|
+
const from = await storePath(selected.bundleRoot, file);
|
|
4234
|
+
const to = await storePath(stage, file, true);
|
|
4235
|
+
await fs17.copyFile(from, to);
|
|
4236
|
+
await fs17.chmod(to, (await fs17.stat(from)).mode & 511);
|
|
4237
|
+
}
|
|
4238
|
+
await verifyBundle(stage, runtime.bundle.manifestHash);
|
|
4239
|
+
} else {
|
|
4240
|
+
const npm = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
4241
|
+
const options = { timeout: 12e4, maxBuffer: 2 * 1024 * 1024, windowsHide: true };
|
|
4242
|
+
const packed = JSON.parse((await exec10(npm, ["pack", "--ignore-scripts", "--json", "--pack-destination", stage], { ...options, cwd: source })).stdout);
|
|
4243
|
+
const filename = packed[0]?.filename;
|
|
4244
|
+
if (typeof filename !== "string" || path22.basename(filename) !== filename) throw new Error("npm did not produce a Mason package archive.");
|
|
4245
|
+
await fs17.rename(path22.join(stage, filename), path22.join(stage, "mason.tgz"));
|
|
4246
|
+
await fs17.writeFile(path22.join(stage, "package.json"), JSON.stringify({ name: "mason-project-runtime", private: true, version: "1.0.0" }));
|
|
4247
|
+
await exec10(npm, ["install", "--ignore-scripts", "--omit=dev", "--no-audit", "--no-fund", "--save-exact", "./mason.tgz"], { ...options, cwd: stage });
|
|
4248
|
+
for (const file of BINARIES) {
|
|
4249
|
+
if (checksum(await fs17.readFile(path22.join(stage, "node_modules/mason-context", file))) !== runtime.hashes[file]) throw new Error("Installed Mason binary differs from the selected distribution.");
|
|
4250
|
+
}
|
|
4124
4251
|
}
|
|
4125
|
-
await
|
|
4252
|
+
await fs17.writeFile(path22.join(stage, "receipt.json"), JSON.stringify(runtime, null, 2) + "\n");
|
|
4126
4253
|
try {
|
|
4127
|
-
await
|
|
4254
|
+
await fs17.rename(target, target + ".previous-" + randomUUID6());
|
|
4128
4255
|
} catch (error) {
|
|
4129
4256
|
if (error.code !== "ENOENT") throw error;
|
|
4130
4257
|
}
|
|
4131
|
-
await
|
|
4258
|
+
await fs17.rename(stage, target);
|
|
4132
4259
|
if (!await verifyRuntime(root, runtime)) throw new Error("The installed Mason runtime could not be verified.");
|
|
4133
4260
|
return runtime;
|
|
4134
4261
|
} finally {
|
|
4135
|
-
await
|
|
4262
|
+
await fs17.rm(stage, { recursive: true, force: true });
|
|
4136
4263
|
}
|
|
4137
4264
|
}
|
|
4138
4265
|
var exec10, checksum, BINARIES;
|
|
@@ -4142,8 +4269,9 @@ var init_runtime2 = __esm({
|
|
|
4142
4269
|
init_model();
|
|
4143
4270
|
init_storage();
|
|
4144
4271
|
init_evidence();
|
|
4272
|
+
init_bundle();
|
|
4145
4273
|
exec10 = promisify10(execFile10);
|
|
4146
|
-
checksum = (bytes) =>
|
|
4274
|
+
checksum = (bytes) => createHash5("sha256").update(bytes).digest("hex");
|
|
4147
4275
|
BINARIES = ["dist/mason-auto.js", "dist/mason-mcp.js"];
|
|
4148
4276
|
}
|
|
4149
4277
|
});
|
|
@@ -4172,17 +4300,18 @@ async function setupStatus(dir) {
|
|
|
4172
4300
|
};
|
|
4173
4301
|
}
|
|
4174
4302
|
const hosts = {};
|
|
4175
|
-
const
|
|
4303
|
+
const commonLauncherCurrent = await readText(ws.root, ".mason/run.cjs") === LAUNCHER;
|
|
4176
4304
|
const automation = await automationStatus(ws.root);
|
|
4177
4305
|
for (const host of ["codex", "claude"]) {
|
|
4178
4306
|
const entry = setup.hosts[host];
|
|
4179
4307
|
if (!entry) continue;
|
|
4308
|
+
const launcherCurrent = commonLauncherCurrent && (!entry.runtime.bundle || await readText(ws.root, ".mason/run.sh") === SHELL_LAUNCHER && await readText(ws.root, ".mason/run.ps1") === POWERSHELL_LAUNCHER && await readText(ws.root, `.mason/runtime/${host}.txt`) === entry.runtime.id + "\n");
|
|
4180
4309
|
const instructions = await instructionEdits(ws.root, host);
|
|
4181
4310
|
const instructionsCurrent = instructions.every((edit) => edit.before === edit.after);
|
|
4182
4311
|
const installed = await verifyRuntime(ws.root, entry.runtime);
|
|
4183
|
-
const config = await inspectHostConfig(ws.root, host);
|
|
4184
|
-
const mcp = !config.mcpDisabled && hash(config.mcp) === entry.mcpFingerprint && isDeepStrictEqual2({ command: config.mcp?.command, args: config.mcp?.args }, mcpCommand(host));
|
|
4185
|
-
const hooks = isDeepStrictEqual2(config.hooks, hookConfig(host, hookCommand(host)).hooks) && !config.disabled;
|
|
4312
|
+
const config = await inspectHostConfig(ws.root, host, void 0, entry.runtime);
|
|
4313
|
+
const mcp = !config.mcpDisabled && hash(config.mcp) === entry.mcpFingerprint && isDeepStrictEqual2({ command: config.mcp?.command, args: config.mcp?.args }, mcpCommand(host, entry.runtime));
|
|
4314
|
+
const hooks = isDeepStrictEqual2(config.hooks, hookConfig(host, hookCommand(host, entry.runtime)).hooks) && !config.disabled;
|
|
4186
4315
|
const local = await loadSetupReceipt(ws.root, ws.directory, host);
|
|
4187
4316
|
const configured = local?.status === "configured" && local.root === ws.root && local.revision === entry.revision;
|
|
4188
4317
|
const observation = await readObservation(ws.root, ws.directory, host, entry.revision);
|
|
@@ -4276,15 +4405,15 @@ async function setupProject(dir, options = {}) {
|
|
|
4276
4405
|
const existing = await loadSetup(ws.root);
|
|
4277
4406
|
const previousReceipt = await loadSetupReceipt(ws.root, ws.directory, host);
|
|
4278
4407
|
const marker = await loadProjectMarker(ws.root);
|
|
4408
|
+
const selected = await sourceRuntime();
|
|
4279
4409
|
const instructions = await instructionEdits(ws.root, host);
|
|
4280
|
-
const mcp = await mcpEdit(ws.root, host);
|
|
4281
|
-
const hooks = await hookEdits(ws.root, host);
|
|
4410
|
+
const mcp = await mcpEdit(ws.root, host, selected.runtime);
|
|
4411
|
+
const hooks = await hookEdits(ws.root, host, selected.runtime);
|
|
4282
4412
|
const ancillary = await ancillaryEdits(ws.root);
|
|
4283
4413
|
const edits = [...ancillary, ...instructions, mcp, ...hooks];
|
|
4284
4414
|
const setupBefore = await readText(ws.root, ".mason/setup.json");
|
|
4285
|
-
const
|
|
4286
|
-
const
|
|
4287
|
-
const desiredHooks = hookConfig(host, hookCommand(host)).hooks;
|
|
4415
|
+
const mcpFingerprint = hash((await inspectHostConfig(ws.root, host, mcp.after, selected.runtime)).mcp);
|
|
4416
|
+
const desiredHooks = hookConfig(host, hookCommand(host, selected.runtime)).hooks;
|
|
4288
4417
|
const fingerprint = hash({
|
|
4289
4418
|
runtime: selected.runtime,
|
|
4290
4419
|
launcher: LAUNCHER,
|
|
@@ -4312,6 +4441,10 @@ async function setupProject(dir, options = {}) {
|
|
|
4312
4441
|
revision
|
|
4313
4442
|
});
|
|
4314
4443
|
await installRuntime(ws.root, selected);
|
|
4444
|
+
if (selected.runtime.bundle) {
|
|
4445
|
+
const pointer = `.mason/runtime/${host}.txt`;
|
|
4446
|
+
await applyEdit(ws.root, { path: pointer, before: await readText(ws.root, pointer), after: selected.runtime.id + "\n" });
|
|
4447
|
+
}
|
|
4315
4448
|
const changedFiles = [];
|
|
4316
4449
|
for (const edit of edits) if (await applyEdit(ws.root, edit)) changedFiles.push(edit.path);
|
|
4317
4450
|
if (await applyEdit(ws.root, { path: ".mason/setup.json", before: setupBefore, after: JSON.stringify(setup, null, 2) + "\n" })) changedFiles.push(".mason/setup.json");
|
|
@@ -4320,7 +4453,7 @@ async function setupProject(dir, options = {}) {
|
|
|
4320
4453
|
changedFiles.push(".mason/project.json");
|
|
4321
4454
|
}
|
|
4322
4455
|
const checked = await automate(ws.root, { event: "turn_start" });
|
|
4323
|
-
const configured = await inspectHostConfig(ws.root, host);
|
|
4456
|
+
const configured = await inspectHostConfig(ws.root, host, void 0, selected.runtime);
|
|
4324
4457
|
await writeStoreJson(ws.root, receiptPath, {
|
|
4325
4458
|
version: 1,
|
|
4326
4459
|
host,
|