agentic-workflow-manager 9.7.0 → 9.8.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/README.md +5 -1
- package/dist/src/commands/doctor.js +4 -2
- package/dist/src/commands/export.js +3 -0
- package/dist/src/commands/init.js +10 -2
- package/dist/src/commands/job/exec-wrapper.js +53 -3
- package/dist/src/commands/plan/index.js +124 -6
- package/dist/src/commands/registry/add.js +2 -2
- package/dist/src/commands/registry/index.js +7 -5
- package/dist/src/commands/registry/install-bundles.js +4 -4
- package/dist/src/commands/registry/status.js +4 -4
- package/dist/src/commands/sensors/bootstrap.js +36 -5
- package/dist/src/commands/sensors/compatibility/contract.js +19 -4
- package/dist/src/commands/sensors/compatibility/discovery.js +23 -3
- package/dist/src/commands/sensors/compatibility/live.js +18 -1
- package/dist/src/commands/sensors/compatibility/probe.js +8 -2
- package/dist/src/commands/sensors/compatibility/resolve.js +5 -2
- package/dist/src/commands/sensors/coverage/evidence.js +19 -10
- package/dist/src/commands/sensors/coverage/index.js +11 -2
- package/dist/src/commands/sensors/coverage/resolve.js +10 -0
- package/dist/src/commands/sensors/exec.js +16 -1
- package/dist/src/commands/sensors/migrate.js +62 -2
- package/dist/src/commands/sensors/prepare.js +2 -0
- package/dist/src/commands/sensors/result.js +2 -0
- package/dist/src/commands/sensors/run.js +64 -1
- package/dist/src/commands/sensors/status.js +6 -4
- package/dist/src/commands/sync.js +2 -1
- package/dist/src/commands/update.js +3 -1
- package/dist/src/commands/watch/archive-unused.js +138 -0
- package/dist/src/commands/watch/generations.js +32 -9
- package/dist/src/commands/watch/index.js +108 -2
- package/dist/src/commands/watch/init.js +149 -18
- package/dist/src/commands/watch/supervisor.js +167 -13
- package/dist/src/commands/watch/teardown-driver.js +13 -13
- package/dist/src/commands/watch/track-state.js +59 -0
- package/dist/src/commands/watch/tracks.js +38 -102
- package/dist/src/core/admission/index.js +181 -0
- package/dist/src/core/admission/registry-contracts.js +290 -0
- package/dist/src/core/bundle-install.js +2 -2
- package/dist/src/core/bundles.js +92 -19
- package/dist/src/core/diagnostics/context.js +1 -1
- package/dist/src/core/export/index.js +1 -1
- package/dist/src/core/export/resolve.js +2 -2
- package/dist/src/core/journal/fingerprint.js +35 -1
- package/dist/src/core/journal/paths.js +11 -0
- package/dist/src/core/journal/store.js +146 -4
- package/dist/src/core/journal/types.js +35 -1
- package/dist/src/core/migration/historical-provenance.js +18 -0
- package/dist/src/core/migration/index.js +322 -0
- package/dist/src/core/plan/identity.js +75 -0
- package/dist/src/core/plan/validate.js +178 -64
- package/dist/src/core/reconciliation.js +1 -1
- package/dist/src/index.js +46 -39
- package/dist/src/utils/grouping.js +1 -1
- package/dist/src/utils/registry-view.js +1 -1
- package/dist/tests/commands/bundle-diagnostics.test.js +145 -0
- package/dist/tests/commands/export.test.js +28 -2
- package/dist/tests/commands/job/exec-wrapper.test.js +16 -0
- package/dist/tests/commands/job/gate-reconcile.test.js +24 -10
- package/dist/tests/commands/multi-agent-targeting.test.js +1 -1
- package/dist/tests/commands/pin.test.js +23 -23
- package/dist/tests/commands/plan/index.test.js +630 -14
- package/dist/tests/commands/sensors/bootstrap.test.js +57 -4
- package/dist/tests/commands/sensors/compatibility/contract.test.js +21 -0
- package/dist/tests/commands/sensors/compatibility/discovery.test.js +19 -0
- package/dist/tests/commands/sensors/compatibility/probe.test.js +14 -1
- package/dist/tests/commands/sensors/compatibility/resolve.test.js +17 -0
- package/dist/tests/commands/sensors/coverage/evidence.test.js +51 -27
- package/dist/tests/commands/sensors/coverage/resolve.test.js +51 -0
- package/dist/tests/commands/sensors/exec.test.js +112 -0
- package/dist/tests/commands/sensors/run-changed.test.js +11 -0
- package/dist/tests/commands/sensors/run.test.js +15 -2
- package/dist/tests/commands/sensors/status.test.js +3 -3
- package/dist/tests/commands/watch/e2e-crash.test.js +126 -11
- package/dist/tests/commands/watch/generations.test.js +113 -4
- package/dist/tests/commands/watch/plan-lifecycle.test.js +263 -0
- package/dist/tests/commands/watch/supervisor-loop.test.js +247 -49
- package/dist/tests/commands/watch/track-finalize.test.js +20 -3
- package/dist/tests/commands/watch/track-freeze.test.js +43 -30
- package/dist/tests/commands/watch/track-state-dependency.test.js +15 -0
- package/dist/tests/commands/watch/watch-init.test.js +244 -0
- package/dist/tests/core/admission/index.test.js +87 -0
- package/dist/tests/core/bundle-install.test.js +36 -0
- package/dist/tests/core/bundles-multiroot.test.js +24 -2
- package/dist/tests/core/bundles-overrides.test.js +10 -7
- package/dist/tests/core/bundles.test.js +75 -6
- package/dist/tests/core/diagnostics/context.test.js +2 -2
- package/dist/tests/core/export/engine.test.js +1 -1
- package/dist/tests/core/export/resolve.test.js +1 -1
- package/dist/tests/core/init/mutation-targets.test.js +1 -1
- package/dist/tests/core/init/orchestrator.test.js +1 -1
- package/dist/tests/core/init/steps.test.js +1 -1
- package/dist/tests/core/journal/fingerprint.test.js +22 -0
- package/dist/tests/core/journal/store.test.js +53 -0
- package/dist/tests/core/journal/types.test.js +7 -0
- package/dist/tests/core/migration/facts.test.js +289 -0
- package/dist/tests/core/migration/historical-provenance.test.js +25 -0
- package/dist/tests/core/plan/validate.test.js +320 -6
- package/dist/tests/core/reconciliation.test.js +1 -1
- package/dist/tests/core/sync-profile-atomicity.test.js +1 -1
- package/dist/tests/integration/pack-e2e.test.js +4 -1
- package/dist/tests/integration/preflight-json-pipe.e2e.test.js +7 -3
- package/dist/tests/integration/sensor-bootstrap.e2e.test.js +37 -0
- package/dist/tests/integration/sensor-structured-run.e2e.test.js +12 -10
- package/dist/tests/providers/injection-config.test.js +14 -6
- package/dist/tests/structural/bundle-skill-reference-contract.test.js +56 -0
- package/dist/tests/structural/jest-environment-is-isolated.test.js +29 -24
- package/dist/tests/structural/native-release-artifacts.test.js +16 -0
- package/dist/tests/utils/grouping.test.js +2 -2
- package/dist/tests/utils/registry-view.test.js +7 -7
- package/package.json +2 -2
- package/prebuilds/darwin-arm64/secure_fs.node +0 -0
- package/prebuilds/darwin-x64/secure_fs.node +0 -0
- package/prebuilds/win32-arm64/secure_fs.node +0 -0
- package/prebuilds/win32-x64/secure_fs.node +0 -0
|
@@ -216,10 +216,21 @@ function parseVariant(input, source, location) {
|
|
|
216
216
|
if (semver_1.default.validRange(toolRange) === null || semver_1.default.validRange(runtimeRange) === null)
|
|
217
217
|
invalid(source, `${location}.requirements ranges must be valid semver ranges`);
|
|
218
218
|
const probe = policy ? undefined : record(value.probe, source, `${location}.probe`);
|
|
219
|
+
let probeScript;
|
|
219
220
|
if (probe) {
|
|
220
|
-
fields(probe, ['kind'], source, `${location}.probe`);
|
|
221
|
+
fields(probe, ['kind', 'script'], source, `${location}.probe`);
|
|
221
222
|
if (typeof probe.kind !== 'string' || !ALLOWED_PROBES.has(probe.kind))
|
|
222
223
|
invalid(source, `${location}.probe.kind must be an allowed probe`);
|
|
224
|
+
// Historical schema-v2 packs remain readable. An unnamed probe cannot
|
|
225
|
+
// certify compatibility; the probe runner rejects it as unverifiable.
|
|
226
|
+
if (probe.kind === 'package-script-present' && 'script' in probe) {
|
|
227
|
+
probeScript = text(probe.script, source, `${location}.probe.script`);
|
|
228
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9:_-]*$/.test(probeScript))
|
|
229
|
+
invalid(source, `${location}.probe.script must be a package script name`);
|
|
230
|
+
}
|
|
231
|
+
else if (probe.kind !== 'package-script-present' && 'script' in probe) {
|
|
232
|
+
invalid(source, `${location}.probe.script is only valid for package-script-present`);
|
|
233
|
+
}
|
|
223
234
|
}
|
|
224
235
|
const changedCommand = 'changedCommand' in value ? parseStructuredCommand(value.changedCommand, source) : undefined;
|
|
225
236
|
if (changedCommand && !changedCommand.fileInput)
|
|
@@ -234,7 +245,7 @@ function parseVariant(input, source, location) {
|
|
|
234
245
|
invalid(source, `${location}.requirements.packageJsonFields[${index}] must be a stable package.json field`); return field; }) } : {}) },
|
|
235
246
|
assets: assetArray(value.assets, source, `${location}.assets`, true),
|
|
236
247
|
formatter: text(value.formatter, source, `${location}.formatter`),
|
|
237
|
-
probe: { kind: policy?.probe ?? probe.kind },
|
|
248
|
+
probe: { kind: policy?.probe ?? probe.kind, ...(probeScript ? { script: probeScript } : {}) },
|
|
238
249
|
...(policy ? { policyRef: SEMGREP_POLICY_REF } : {}),
|
|
239
250
|
command: parseStructuredCommand(value.command, source),
|
|
240
251
|
...(changedCommand ? { changedCommand } : {}),
|
|
@@ -295,8 +306,12 @@ function parseSensor(input, source, location, variantIds) {
|
|
|
295
306
|
applicability.allFiles = stringArray(applicabilityInput.allFiles, source, `${location}.applicability.allFiles`).map((file, index) => asset(file, source, `${location}.applicability.allFiles[${index}]`));
|
|
296
307
|
if ('anyFiles' in applicabilityInput)
|
|
297
308
|
applicability.anyFiles = stringArray(applicabilityInput.anyFiles, source, `${location}.applicability.anyFiles`).map((file, index) => asset(file, source, `${location}.applicability.anyFiles[${index}]`));
|
|
298
|
-
if ('kind' in applicabilityInput)
|
|
299
|
-
|
|
309
|
+
if ('kind' in applicabilityInput) {
|
|
310
|
+
const kind = text(applicabilityInput.kind, source, `${location}.applicability.kind`);
|
|
311
|
+
if (kind !== 'explicit-or-supported-language' && kind !== 'explicit-opt-in')
|
|
312
|
+
invalid(source, `${location}.applicability.kind must be a supported applicability kind`);
|
|
313
|
+
applicability.kind = kind;
|
|
314
|
+
}
|
|
300
315
|
if (Object.keys(applicability).length === 0)
|
|
301
316
|
invalid(source, `${location}.applicability must declare a condition`);
|
|
302
317
|
if ('fast' in value && typeof value.fast !== 'boolean')
|
|
@@ -176,6 +176,7 @@ function discoverProjectEvidence(cwd, pack, dependencies = {}) {
|
|
|
176
176
|
const declaredManager = typeof pkg?.packageManager === 'string' ? pkg.packageManager.split('@')[0] : null;
|
|
177
177
|
if (declaredManager)
|
|
178
178
|
lockManagers.add(declaredManager);
|
|
179
|
+
const packageManager = declaredManager ?? (lockManagers.size === 1 ? [...lockManagers][0] : null);
|
|
179
180
|
const configCandidates = new Set(COMMON_CONFIGS);
|
|
180
181
|
if ('schemaVersion' in pack) {
|
|
181
182
|
for (const marker of pack.detects)
|
|
@@ -193,16 +194,35 @@ function discoverProjectEvidence(cwd, pack, dependencies = {}) {
|
|
|
193
194
|
const packageJsonFields = Object.keys(pkg ?? {}).filter(field => /^[A-Za-z][A-Za-z0-9]*$/.test(field)).sort();
|
|
194
195
|
const declaredToolRanges = { ...stringMap(pkg?.dependencies), ...stringMap(pkg?.devDependencies), ...stringMap(pkg?.peerDependencies) };
|
|
195
196
|
const tools = new Set();
|
|
197
|
+
const pathTools = new Set();
|
|
196
198
|
if ('schemaVersion' in pack)
|
|
197
199
|
for (const sensor of Object.values(pack.sensors))
|
|
198
|
-
for (const variant of sensor.variants)
|
|
200
|
+
for (const variant of sensor.variants) {
|
|
199
201
|
tools.add(variant.requirements.tool);
|
|
202
|
+
if (variant.command?.resolution === 'path' && variant.command.packageManager === variant.requirements.tool)
|
|
203
|
+
pathTools.add(variant.requirements.tool);
|
|
204
|
+
}
|
|
200
205
|
const environment = pythonEnvironment(root);
|
|
201
206
|
const sitePackages = environment ? pythonSitePackages(root, environment.rootParts, targetPlatform) : [];
|
|
202
|
-
const toolVersions =
|
|
207
|
+
const toolVersions = {};
|
|
208
|
+
const toolProvenance = {};
|
|
209
|
+
for (const tool of [...tools].sort()) {
|
|
210
|
+
if (pathTools.has(tool) && packageManager === tool) {
|
|
211
|
+
// Discovery/status is read-only. A PATH executable is not inspected or
|
|
212
|
+
// executed here; callers that already possess trusted runtime evidence
|
|
213
|
+
// may inject it explicitly for an execution-time compatibility check.
|
|
214
|
+
toolVersions[tool] = exactVersion(dependencies.pathToolVersion?.(tool) ?? null);
|
|
215
|
+
toolProvenance[tool] = toolVersions[tool] === null ? null : 'path';
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
const pythonVersion = pythonToolVersion(root, sitePackages, tool);
|
|
219
|
+
toolVersions[tool] = pythonVersion ?? installedPackageVersion(root, tool);
|
|
220
|
+
toolProvenance[tool] = pythonVersion ? 'python-environment' : toolVersions[tool] ? 'node-modules-bin' : null;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
203
223
|
return {
|
|
204
224
|
cwd: root, os: targetPlatform, runtimeVersions: { node: process.versions.node ?? null, ...(environment ? { python: environment.runtimeVersion } : {}) }, pythonEnvironmentRoot: environment?.rootParts[0] ?? null, declaredToolRanges, toolVersions,
|
|
205
|
-
packageManager
|
|
225
|
+
toolProvenance, packageManager, packageManagerConflict: lockManagers.size > 1,
|
|
206
226
|
scripts, configFiles, packageJsonFields, paths: [...new Set([...(safeFile(root, 'package.json') ? ['package.json'] : []), ...locks, ...configFiles])].sort(),
|
|
207
227
|
};
|
|
208
228
|
}
|
|
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.bindContainedRuntimeCommands = bindContainedRuntimeCommands;
|
|
6
7
|
exports.resolveLiveCompatibility = resolveLiveCompatibility;
|
|
7
8
|
exports.resolveParsedPackCompatibility = resolveParsedPackCompatibility;
|
|
8
9
|
const pack_source_1 = require("./pack-source");
|
|
@@ -11,6 +12,22 @@ const discovery_1 = require("./discovery");
|
|
|
11
12
|
const resolve_1 = require("./resolve");
|
|
12
13
|
const probe_1 = require("./probe");
|
|
13
14
|
const path_1 = __importDefault(require("path"));
|
|
15
|
+
const child_process_1 = require("child_process");
|
|
16
|
+
const semver_1 = __importDefault(require("semver"));
|
|
17
|
+
/** Execution-time evidence only. Status/discovery never invokes this resolver. */
|
|
18
|
+
function pathPackageManagerVersion(tool) {
|
|
19
|
+
if (!['npm', 'pnpm', 'yarn', 'bun'].includes(tool))
|
|
20
|
+
return null;
|
|
21
|
+
try {
|
|
22
|
+
const result = (0, child_process_1.spawnSync)(tool, ['--version'], { cwd: process.cwd(), shell: false, encoding: 'utf8', timeout: 5_000, maxBuffer: 1024, windowsHide: true, stdio: 'pipe' });
|
|
23
|
+
if (result.error || result.status !== 0 || result.signal || typeof result.stdout !== 'string' || result.stdout.length > 256)
|
|
24
|
+
return null;
|
|
25
|
+
return semver_1.default.valid(result.stdout.trim());
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
14
31
|
/**
|
|
15
32
|
* Python package metadata is evidence only for a contained virtual environment.
|
|
16
33
|
* A v2 command that names a Python-runtime tool must therefore resolve through
|
|
@@ -68,7 +85,7 @@ async function resolveParsedPackCompatibility(cwd, pack, options = {}) {
|
|
|
68
85
|
throw new Error('cwd must be a non-empty path');
|
|
69
86
|
if (!pack || typeof pack !== 'object' || pack.schemaVersion !== 2)
|
|
70
87
|
throw new Error('pack must be a parsed v2 sensor pack');
|
|
71
|
-
const evidence = (0, discovery_1.discoverProjectEvidence)(cwd, pack);
|
|
88
|
+
const evidence = (0, discovery_1.discoverProjectEvidence)(cwd, pack, { pathToolVersion: pathPackageManagerVersion });
|
|
72
89
|
const executionPack = bindContainedRuntimeCommands(pack, evidence.pythonEnvironmentRoot);
|
|
73
90
|
const resolutionEvidence = { ...evidence, ...(options.packSelection === 'explicit' ? { packSelection: 'explicit' } : {}) };
|
|
74
91
|
const initial = (0, resolve_1.resolveProjectCompatibility)(executionPack, resolutionEvidence).sensors;
|
|
@@ -48,8 +48,14 @@ async function runCompatibilityProbe(probe, evidence, executor = exec_1.runStruc
|
|
|
48
48
|
throw new Error('compatibility probe kind must be allowed');
|
|
49
49
|
if (!evidence || typeof evidence.cwd !== 'string' || evidence.cwd.trim() === '')
|
|
50
50
|
throw new Error('probe evidence requires cwd');
|
|
51
|
-
if (probe.kind === 'package-script-present')
|
|
52
|
-
|
|
51
|
+
if (probe.kind === 'package-script-present') {
|
|
52
|
+
const script = probe.script;
|
|
53
|
+
if (!Object.prototype.hasOwnProperty.call(probe, 'script'))
|
|
54
|
+
return { status: 'unverifiable', reason: 'package-script-name-required' };
|
|
55
|
+
if (typeof script !== 'string' || !/^[A-Za-z0-9][A-Za-z0-9:_-]*$/.test(script))
|
|
56
|
+
throw new Error('package-script-present probe requires a named package script');
|
|
57
|
+
return { status: evidence.scripts?.includes(script) ? 'matched' : 'not-matched', reason: 'package-script' };
|
|
58
|
+
}
|
|
53
59
|
if (probe.kind === 'config-present')
|
|
54
60
|
return { status: (evidence.configFiles?.length ?? 0) > 0 ? 'matched' : 'not-matched', reason: 'config-file' };
|
|
55
61
|
try {
|
|
@@ -26,7 +26,7 @@ function result(state, reason, variant, evidence) {
|
|
|
26
26
|
return { state, reason, variantId: variant?.id ?? null, toolVersion, runtimeVersion, certifiedRange: variant?.certifiedRange ?? null, evidence: refs.slice(0, 32) };
|
|
27
27
|
}
|
|
28
28
|
function applies(sensor, evidence) {
|
|
29
|
-
if (sensor.applicability.kind === 'explicit-or-supported-language') {
|
|
29
|
+
if (sensor.applicability.kind === 'explicit-or-supported-language' || sensor.applicability.kind === 'explicit-opt-in') {
|
|
30
30
|
return evidence.applicable === true || evidence.packSelection === 'explicit';
|
|
31
31
|
}
|
|
32
32
|
if (evidence.applicable === false)
|
|
@@ -40,7 +40,10 @@ function specificity(variant) {
|
|
|
40
40
|
}
|
|
41
41
|
function validVersion(value) { return typeof value === 'string' && semver_1.default.valid(value) !== null; }
|
|
42
42
|
function toolFor(variant, evidence) {
|
|
43
|
-
|
|
43
|
+
const tool = variant.requirements.tool;
|
|
44
|
+
const version = evidence.toolVersions === undefined ? evidence.toolVersion : evidence.toolVersions[tool];
|
|
45
|
+
const provenance = evidence.toolProvenance?.[tool];
|
|
46
|
+
return provenance !== undefined && provenance !== null && provenance !== variant.command.resolution ? null : version;
|
|
44
47
|
}
|
|
45
48
|
function runtimeFor(variant, evidence) {
|
|
46
49
|
return evidence.runtimeVersions === undefined ? evidence.runtimeVersion : evidence.runtimeVersions[variant.requirements.runtime];
|
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.observeDetector = observeDetector;
|
|
7
7
|
const fs_1 = __importDefault(require("fs"));
|
|
8
8
|
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const native_bridge_1 = require("../../../core/secure-fs/native-bridge");
|
|
9
10
|
const contract_1 = require("./contract");
|
|
10
11
|
const realIo = {
|
|
11
12
|
lstatSync: fs_1.default.lstatSync,
|
|
@@ -39,23 +40,31 @@ function inspectFile(root, relative, markers, io) {
|
|
|
39
40
|
return { status: 'unverifiable', evidence: [{ kind: 'file', path: relative, status: 'unverifiable' }] };
|
|
40
41
|
}
|
|
41
42
|
const noFollow = fs_1.default.constants.O_NOFOLLOW;
|
|
42
|
-
if (typeof noFollow !== 'number') {
|
|
43
|
+
if (typeof noFollow !== 'number' && io !== realIo) {
|
|
43
44
|
return { status: 'unverifiable', evidence: [{ kind: 'file', path: relative, status: 'unverifiable' }] };
|
|
44
45
|
}
|
|
45
46
|
let fd;
|
|
46
47
|
let content;
|
|
47
48
|
try {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
if (typeof noFollow !== 'number') {
|
|
50
|
+
// Windows has no O_NOFOLLOW. Its native descriptor-anchored read
|
|
51
|
+
// rejects reparse points in every ancestor and preserves identity.
|
|
52
|
+
// Never replace an injected IO boundary with this native trust.
|
|
53
|
+
content = native_bridge_1.secureFs.readRegularFile(absolute, contract_1.MAX_COVERAGE_FILE_BYTES).bytes.toString('utf8');
|
|
52
54
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
else {
|
|
56
|
+
fd = (io.openSync ?? realIo.openSync)(absolute, fs_1.default.constants.O_RDONLY | noFollow);
|
|
57
|
+
const opened = (io.fstatSync ?? realIo.fstatSync)(fd);
|
|
58
|
+
if (!opened.isFile() || opened.size > contract_1.MAX_COVERAGE_FILE_BYTES) {
|
|
59
|
+
return { status: 'unverifiable', evidence: [{ kind: 'file', path: relative, status: 'unverifiable' }] };
|
|
60
|
+
}
|
|
61
|
+
const buffer = Buffer.allocUnsafe(contract_1.MAX_COVERAGE_FILE_BYTES + 1);
|
|
62
|
+
const bytesRead = (io.readSync ?? realIo.readSync)(fd, buffer, 0, buffer.length, null);
|
|
63
|
+
if (!Number.isSafeInteger(bytesRead) || bytesRead < 0 || bytesRead > contract_1.MAX_COVERAGE_FILE_BYTES) {
|
|
64
|
+
return { status: 'unverifiable', evidence: [{ kind: 'file', path: relative, status: 'unverifiable' }] };
|
|
65
|
+
}
|
|
66
|
+
content = buffer.subarray(0, bytesRead).toString('utf8');
|
|
57
67
|
}
|
|
58
|
-
content = buffer.subarray(0, bytesRead).toString('utf8');
|
|
59
68
|
}
|
|
60
69
|
catch {
|
|
61
70
|
return { status: 'unverifiable', evidence: [{ kind: 'file', path: relative, status: 'unverifiable' }] };
|
|
@@ -12,6 +12,7 @@ const manifest_1 = require("../compatibility/manifest");
|
|
|
12
12
|
const scan_1 = require("../../../core/ledger/scan");
|
|
13
13
|
const empirical_1 = require("./empirical");
|
|
14
14
|
const path_1 = __importDefault(require("path"));
|
|
15
|
+
const project_1 = require("../project");
|
|
15
16
|
const defaults = { resolve: resolve_1.resolveCoverageInputs, observe: evidence_1.observeDetector, resolveLive: live_1.resolveLiveCompatibility, scan: scan_1.scanProjectLedgers };
|
|
16
17
|
function normalizedMin(value) {
|
|
17
18
|
if (value === undefined)
|
|
@@ -34,13 +35,21 @@ async function runCoverage(cwd, dependencies = {}, options = {}) {
|
|
|
34
35
|
return { schemaVersion: 2, pack: null, registry: null, overall: 'inconclusive', static: { status: 'inconclusive', reason: 'not_configured', classes: [] },
|
|
35
36
|
empirical: empirical(deps, path_1.default.resolve(cwd), {}, min, 'unavailable') };
|
|
36
37
|
}
|
|
38
|
+
const packageRoot = input.manifest.kind !== 'legacy' && input.manifest.pack.packageRoot
|
|
39
|
+
? (() => {
|
|
40
|
+
const project = (0, project_1.resolveSensorProject)(input.projectRoot);
|
|
41
|
+
if (project.state !== 'configured')
|
|
42
|
+
throw new Error(`runCoverage: packageRoot cannot be resolved: ${project.state === 'invalid' ? project.reason : 'manifest missing'}`);
|
|
43
|
+
return project.packageRoot;
|
|
44
|
+
})()
|
|
45
|
+
: input.projectRoot;
|
|
37
46
|
if (input.kind === 'no_reference') {
|
|
38
47
|
return { schemaVersion: 2, pack: input.pack, registry: input.registry, overall: 'inconclusive', static: { status: 'inconclusive', reason: 'no_reference', classes: [] },
|
|
39
48
|
empirical: empirical(deps, input.projectRoot, {}, min, 'unavailable') };
|
|
40
49
|
}
|
|
41
50
|
let live;
|
|
42
51
|
if (input.manifest.kind !== 'legacy') {
|
|
43
|
-
live = (await deps.resolveLive(
|
|
52
|
+
live = (await deps.resolveLive(packageRoot, input.pack, input.registryRoot)).sensors;
|
|
44
53
|
}
|
|
45
54
|
else {
|
|
46
55
|
live = Object.fromEntries(Object.keys(input.manifest.pack.sensors).map((name) => [name, (0, manifest_1.legacyCompatibility)('legacy manifest without schemaVersion')]));
|
|
@@ -48,7 +57,7 @@ async function runCoverage(cwd, dependencies = {}, options = {}) {
|
|
|
48
57
|
const observations = [];
|
|
49
58
|
for (const [classId, coverageClass] of Object.entries(input.contract.classes)) {
|
|
50
59
|
coverageClass.detectors.forEach((detector, detectorIndex) => {
|
|
51
|
-
const observed = deps.observe(
|
|
60
|
+
const observed = deps.observe(packageRoot, classId, detectorIndex, detector, input.manifest.pack.sensors[detector.sensor]);
|
|
52
61
|
observations.push({
|
|
53
62
|
...observed,
|
|
54
63
|
compatibility: live[detector.sensor] ?? {
|
|
@@ -70,6 +70,16 @@ function findManifestDirNoFollow(startCwd) {
|
|
|
70
70
|
if (error.code !== 'ENOENT')
|
|
71
71
|
throw readFailure(manifestPath, error);
|
|
72
72
|
}
|
|
73
|
+
const gitMarker = path_1.default.join(dir, '.git');
|
|
74
|
+
try {
|
|
75
|
+
const marker = fs_1.default.lstatSync(gitMarker);
|
|
76
|
+
if (marker.isFile() || marker.isDirectory())
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
if (error.code !== 'ENOENT')
|
|
81
|
+
throw readFailure(gitMarker, error);
|
|
82
|
+
}
|
|
73
83
|
const parent = path_1.default.dirname(dir);
|
|
74
84
|
if (parent === dir)
|
|
75
85
|
return null;
|
|
@@ -98,6 +98,20 @@ function validateOptions(opts) {
|
|
|
98
98
|
if (opts.killGraceMs !== undefined && (!Number.isSafeInteger(opts.killGraceMs) || opts.killGraceMs < 0))
|
|
99
99
|
throw new Error('exec options killGraceMs must be a non-negative safe integer');
|
|
100
100
|
}
|
|
101
|
+
/** Run sensor commands with the same Node runtime that launched AWM. Package
|
|
102
|
+
* manager wrappers resolve `node` through PATH, so an older inherited Node can
|
|
103
|
+
* otherwise run a sensor even when the CLI itself is running on a supported
|
|
104
|
+
* runtime. Keep every inherited entry after the runtime directory: tools and
|
|
105
|
+
* project-local shims remain available to the child. */
|
|
106
|
+
function sensorEnvironment(inputEnvironment) {
|
|
107
|
+
const runtimeDir = path_1.default.dirname(process.execPath);
|
|
108
|
+
const inheritedPath = process.env.PATH;
|
|
109
|
+
return {
|
|
110
|
+
...process.env,
|
|
111
|
+
PATH: inheritedPath ? `${runtimeDir}${path_1.default.delimiter}${inheritedPath}` : runtimeDir,
|
|
112
|
+
...inputEnvironment,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
101
115
|
function collectSpawn(input, opts) {
|
|
102
116
|
validateOptions(opts);
|
|
103
117
|
const maxBuffer = opts.maxBuffer ?? DEFAULT_MAX_BUFFER;
|
|
@@ -164,6 +178,7 @@ function collectSpawn(input, opts) {
|
|
|
164
178
|
shell: true,
|
|
165
179
|
cwd: opts.cwd,
|
|
166
180
|
detached: !(0, paths_1.isWindowsNative)(),
|
|
181
|
+
env: sensorEnvironment(input.environment),
|
|
167
182
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
168
183
|
})
|
|
169
184
|
: (0, child_process_1.spawn)(prlimit ?? input.executable, prlimit
|
|
@@ -172,7 +187,7 @@ function collectSpawn(input, opts) {
|
|
|
172
187
|
shell: false,
|
|
173
188
|
cwd: opts.cwd,
|
|
174
189
|
detached: !(0, paths_1.isWindowsNative)(),
|
|
175
|
-
|
|
190
|
+
env: sensorEnvironment(input.environment),
|
|
176
191
|
// stdin closed: a sensor must never block waiting for input, and the
|
|
177
192
|
// EOF also tells watch-mode-capable tools (vitest, jest) to run once.
|
|
178
193
|
stdio: prlimit ? ['ignore', stdoutFd, stderrFd] : ['ignore', 'pipe', 'pipe'],
|
|
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.planV2Migration = planV2Migration;
|
|
7
7
|
exports.replaceV2ManifestWithV3 = replaceV2ManifestWithV3;
|
|
8
|
+
exports.replaceLegacyManifestWithV3 = replaceLegacyManifestWithV3;
|
|
8
9
|
const fs_1 = __importDefault(require("fs"));
|
|
9
10
|
const path_1 = __importDefault(require("path"));
|
|
10
11
|
const contract_1 = require("./compatibility/contract");
|
|
@@ -67,7 +68,7 @@ function exactLogicalSource(input, pack) {
|
|
|
67
68
|
throw new Error('v2 migration source must contain an exact resolved pack');
|
|
68
69
|
let parsedSource;
|
|
69
70
|
try {
|
|
70
|
-
parsedSource = (0, contract_1.parseSensorPack)(JSON.parse(source.content),
|
|
71
|
+
parsedSource = (0, contract_1.parseSensorPack)(JSON.parse(source.content), source.path);
|
|
71
72
|
}
|
|
72
73
|
catch {
|
|
73
74
|
throw new Error('v2 migration source exact v2 pack is invalid');
|
|
@@ -79,7 +80,7 @@ function exactLogicalSource(input, pack) {
|
|
|
79
80
|
function assertSourceCompatibleWithManifest(source, manifest) {
|
|
80
81
|
let parsedSource;
|
|
81
82
|
try {
|
|
82
|
-
parsedSource = (0, contract_1.parseSensorPack)(JSON.parse(source.content),
|
|
83
|
+
parsedSource = (0, contract_1.parseSensorPack)(JSON.parse(source.content), source.path);
|
|
83
84
|
}
|
|
84
85
|
catch {
|
|
85
86
|
throw new Error('v2 migration source exact v2 pack is invalid');
|
|
@@ -94,6 +95,23 @@ function assertSourceCompatibleWithManifest(source, manifest) {
|
|
|
94
95
|
}
|
|
95
96
|
}
|
|
96
97
|
}
|
|
98
|
+
function assertSourceCompatibleWithCandidate(source, candidate) {
|
|
99
|
+
let parsedSource;
|
|
100
|
+
try {
|
|
101
|
+
parsedSource = (0, contract_1.parseSensorPack)(JSON.parse(source.content), source.path);
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
throw new Error('legacy migration source exact v2 pack is invalid');
|
|
105
|
+
}
|
|
106
|
+
if (parsedSource.kind !== 'v2' || parsedSource.pack.name !== candidate.pack) {
|
|
107
|
+
throw new Error('legacy migration source must contain the exact v2 pack');
|
|
108
|
+
}
|
|
109
|
+
for (const [name, sensor] of Object.entries(candidate.sensors)) {
|
|
110
|
+
if (!parsedSource.pack.sensors[name]?.variants.some(variant => variant.id === sensor.variantId)) {
|
|
111
|
+
throw new Error('legacy migration source is not compatible with selected manifest sensors');
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
97
115
|
function hasPhysicalSensorPath(value, registryRoots) {
|
|
98
116
|
if (typeof value === 'string') {
|
|
99
117
|
const normalized = path_1.default.posix.normalize(value.replace(/\\/g, '/')).toLowerCase();
|
|
@@ -207,3 +225,45 @@ function replaceV2ManifestWithV3(manifestPath, candidate, source) {
|
|
|
207
225
|
});
|
|
208
226
|
});
|
|
209
227
|
}
|
|
228
|
+
/**
|
|
229
|
+
* Replace an explicitly bootstrapped legacy manifest with the v3 declaration
|
|
230
|
+
* selected from one current logical pack. Legacy commands have no structured
|
|
231
|
+
* variant identity, so equivalence cannot be asserted; the caller must have
|
|
232
|
+
* re-probed every selected v3 sensor before this fenced publication.
|
|
233
|
+
*/
|
|
234
|
+
function replaceLegacyManifestWithV3(manifestPath, candidate, source) {
|
|
235
|
+
if (typeof manifestPath !== 'string' || !path_1.default.isAbsolute(manifestPath) || path_1.default.normalize(manifestPath) !== manifestPath
|
|
236
|
+
|| manifestPath.includes('\0') || path_1.default.basename(manifestPath) !== 'sensors.json'
|
|
237
|
+
|| path_1.default.basename(path_1.default.dirname(manifestPath)) !== '.awm') {
|
|
238
|
+
throw new Error('legacy migration manifest path must be the canonical project manifest <project>/.awm/sensors.json');
|
|
239
|
+
}
|
|
240
|
+
const manifestDirectory = path_1.default.dirname(manifestPath);
|
|
241
|
+
const projectRoot = path_1.default.dirname(manifestDirectory);
|
|
242
|
+
(0, safe_file_1.withProjectLease)(projectRoot, () => {
|
|
243
|
+
const originalFailure = (_reason) => new Error('legacy migration original manifest must be readable JSON');
|
|
244
|
+
let inspected;
|
|
245
|
+
let original;
|
|
246
|
+
try {
|
|
247
|
+
inspected = inspectManifest(manifestPath, originalFailure);
|
|
248
|
+
original = JSON.parse(inspected.content.toString('utf8'));
|
|
249
|
+
}
|
|
250
|
+
catch {
|
|
251
|
+
throw new Error('legacy migration original manifest must be readable JSON');
|
|
252
|
+
}
|
|
253
|
+
const before = (0, manifest_1.parseSensorManifest)(original, manifestPath);
|
|
254
|
+
const after = (0, manifest_1.parseSensorManifest)(candidate, 'legacy migration candidate');
|
|
255
|
+
if (before.kind !== 'legacy' || after.kind !== 'v3' || after.pack.mode !== 'project-sensors') {
|
|
256
|
+
throw new Error('legacy migration candidate semantic mismatch');
|
|
257
|
+
}
|
|
258
|
+
const logicalSource = exactLogicalSource({ kind: 'logical', source }, after.pack.pack);
|
|
259
|
+
assertSourceCompatibleWithCandidate(logicalSource, after.pack);
|
|
260
|
+
if (after.pack.source.registry !== logicalSource.registry.name || hasPhysicalSensorPath(after.pack.sensors, [logicalSource.registry.contentRoot])
|
|
261
|
+
|| hasPhysicalSensorPath(after.pack.packageRoot, [])) {
|
|
262
|
+
throw new Error('legacy migration candidate contains an unsafe source or physical path');
|
|
263
|
+
}
|
|
264
|
+
assertManifestUnchanged(manifestPath, inspected);
|
|
265
|
+
(0, safe_file_1.writeProjectFile)(projectRoot, '.awm/sensors.json', Buffer.from((0, manifest_1.serializeManifestV3)(after.pack), 'utf8'), {
|
|
266
|
+
mode: 'replace', expected: inspected.content, expectedIdentity: inspected.identity, createParents: false,
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
}
|
|
@@ -30,6 +30,8 @@ function validateRunOptions(opts) {
|
|
|
30
30
|
throw new Error('run option ignoreBaseline must be a boolean');
|
|
31
31
|
if (opts.base !== undefined && (typeof opts.base !== 'string' || opts.base.trim() === ''))
|
|
32
32
|
throw new Error('run option base must be a nonempty string');
|
|
33
|
+
if (opts.readOnly !== undefined && typeof opts.readOnly !== 'boolean')
|
|
34
|
+
throw new Error('run option readOnly must be a boolean');
|
|
33
35
|
if (opts.changed && opts.ignoreBaseline) {
|
|
34
36
|
throw new Error('refusing to combine --changed with a baseline capture: a partial run cannot define the accepted set');
|
|
35
37
|
}
|
|
@@ -91,6 +91,8 @@ function interpretResult(prepared, raw) {
|
|
|
91
91
|
return withEvidence({ name: prepared.name, status: 'inconclusive', errors: [], skipReason: reason });
|
|
92
92
|
}
|
|
93
93
|
if (raw.code === 0) {
|
|
94
|
+
if (format === generic_1.parseGenericOutput)
|
|
95
|
+
return withEvidence({ name: prepared.name, status: 'pass', errors: [] });
|
|
94
96
|
const errors = format(raw.stdout);
|
|
95
97
|
return withEvidence({ name: prepared.name, status: errors.length ? 'fail' : 'pass', errors });
|
|
96
98
|
}
|
|
@@ -8,8 +8,10 @@ exports.findManifestDir = findManifestDir;
|
|
|
8
8
|
exports.detectPackDrift = detectPackDrift;
|
|
9
9
|
exports.resolveConcurrency = resolveConcurrency;
|
|
10
10
|
exports.runSensors = runSensors;
|
|
11
|
+
const fs_1 = __importDefault(require("fs"));
|
|
11
12
|
const os_1 = __importDefault(require("os"));
|
|
12
13
|
const path_1 = __importDefault(require("path"));
|
|
14
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
13
15
|
const baseline_1 = require("./baseline");
|
|
14
16
|
const result_1 = require("./result");
|
|
15
17
|
Object.defineProperty(exports, "applyBaseline", { enumerable: true, get: function () { return result_1.applyBaseline; } });
|
|
@@ -30,6 +32,59 @@ const project_1 = require("./project");
|
|
|
30
32
|
const MAX_BUFFER = 64 * 1024 * 1024;
|
|
31
33
|
/** Hard ceiling on parallel sensors: past this, they only contend for the same cores. */
|
|
32
34
|
const MAX_CONCURRENCY = 4;
|
|
35
|
+
const READ_ONLY_MAX_ENTRIES = 250_000;
|
|
36
|
+
const READ_ONLY_MAX_BYTES = 1024 * 1024 * 1024;
|
|
37
|
+
/**
|
|
38
|
+
* A git status only sees tracked and non-ignored paths, while a sensor can
|
|
39
|
+
* mutate ignored runtime files (notably `.awm/`) just as easily. Hash the
|
|
40
|
+
* actual project tree instead. The scan is deliberately bounded: inability to
|
|
41
|
+
* observe the whole declared project means no admission certificate.
|
|
42
|
+
*/
|
|
43
|
+
function filesystemSnapshot(root) {
|
|
44
|
+
try {
|
|
45
|
+
const digest = crypto_1.default.createHash('sha256');
|
|
46
|
+
let entriesSeen = 0;
|
|
47
|
+
let bytesSeen = 0;
|
|
48
|
+
const visit = (directory, relative) => {
|
|
49
|
+
const entries = fs_1.default.readdirSync(directory, { withFileTypes: true })
|
|
50
|
+
.sort((left, right) => left.name.localeCompare(right.name));
|
|
51
|
+
for (const entry of entries) {
|
|
52
|
+
// Git's internal bookkeeping changes as a consequence of normal
|
|
53
|
+
// observation, not project sensor execution. Everything else,
|
|
54
|
+
// including ignored/untracked `.awm`, is evidence-relevant.
|
|
55
|
+
if (relative === '' && entry.name === '.git')
|
|
56
|
+
continue;
|
|
57
|
+
if (++entriesSeen > READ_ONLY_MAX_ENTRIES)
|
|
58
|
+
throw new Error('snapshot-entry-limit');
|
|
59
|
+
const child = path_1.default.join(directory, entry.name);
|
|
60
|
+
const childRelative = relative === '' ? entry.name : `${relative}/${entry.name}`;
|
|
61
|
+
const stat = fs_1.default.lstatSync(child);
|
|
62
|
+
digest.update(`${childRelative}\0${stat.mode}\0${stat.size}\0`);
|
|
63
|
+
if (stat.isDirectory()) {
|
|
64
|
+
digest.update('directory\0');
|
|
65
|
+
visit(child, childRelative);
|
|
66
|
+
}
|
|
67
|
+
else if (stat.isFile()) {
|
|
68
|
+
if ((bytesSeen += stat.size) > READ_ONLY_MAX_BYTES)
|
|
69
|
+
throw new Error('snapshot-byte-limit');
|
|
70
|
+
digest.update('file\0');
|
|
71
|
+
digest.update(fs_1.default.readFileSync(child));
|
|
72
|
+
}
|
|
73
|
+
else if (stat.isSymbolicLink()) {
|
|
74
|
+
digest.update(`symlink\0${fs_1.default.readlinkSync(child)}\0`);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
digest.update(`special\0${stat.rdev}\0`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
visit(root, '');
|
|
82
|
+
return digest.digest('hex');
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
33
88
|
async function resolveLiveFromSource(cwd, source, packSelection) {
|
|
34
89
|
try {
|
|
35
90
|
const parsed = (0, contract_1.parseSensorPack)(JSON.parse(source.content), source.path);
|
|
@@ -141,6 +196,10 @@ async function runSensors(opts = {}) {
|
|
|
141
196
|
return { sensors: [], overall: 'not_certified', mode: 'invalid', reason, projectRoot: project.projectRoot, manifestPath: project.manifestPath, remedy: 'repair-sensor-manifest' };
|
|
142
197
|
}
|
|
143
198
|
const manifestDir = project.projectRoot;
|
|
199
|
+
const beforeReadOnly = opts.readOnly ? filesystemSnapshot(manifestDir) : undefined;
|
|
200
|
+
if (opts.readOnly && beforeReadOnly === null) {
|
|
201
|
+
return { sensors: [], overall: 'not_certified', projectRoot: project.projectRoot, manifestPath: project.manifestPath, mode: 'invalid', reason: 'read-only-observation-unavailable', remedy: 'run admission from a readable git worktree' };
|
|
202
|
+
}
|
|
144
203
|
const parsed = project.manifest;
|
|
145
204
|
const authority = { projectRoot: project.projectRoot, manifestPath: project.manifestPath };
|
|
146
205
|
if (parsed.kind === 'v3' && (parsed.pack.mode === 'native-gate' || parsed.pack.mode === 'opt-out')) {
|
|
@@ -205,7 +264,7 @@ async function runSensors(opts = {}) {
|
|
|
205
264
|
overall = 'not_certified';
|
|
206
265
|
if (overall === 'skipped' && drift && drift.detection.pack !== 'generic')
|
|
207
266
|
overall = 'not_certified';
|
|
208
|
-
|
|
267
|
+
const output = {
|
|
209
268
|
sensors: results,
|
|
210
269
|
overall,
|
|
211
270
|
...authority,
|
|
@@ -216,4 +275,8 @@ async function runSensors(opts = {}) {
|
|
|
216
275
|
...(drift?.drift ? { packDrift: drift.drift } : {}),
|
|
217
276
|
...(changed ? { changedScope: { files: changed.files.length, ...(changed.error ? { error: changed.error } : {}) } } : {}),
|
|
218
277
|
};
|
|
278
|
+
if (opts.readOnly && filesystemSnapshot(manifestDir) !== beforeReadOnly) {
|
|
279
|
+
return { ...output, overall: 'not_certified', reason: 'read-only-mutation-detected', remedy: 'sensor execution changed the worktree; repair it before using this evidence for admission' };
|
|
280
|
+
}
|
|
281
|
+
return output;
|
|
219
282
|
}
|
|
@@ -9,6 +9,7 @@ const path_1 = __importDefault(require("path"));
|
|
|
9
9
|
const paths_1 = require("../../core/paths");
|
|
10
10
|
const contract_1 = require("./compatibility/contract");
|
|
11
11
|
const discovery_1 = require("./compatibility/discovery");
|
|
12
|
+
const live_1 = require("./compatibility/live");
|
|
12
13
|
const resolve_1 = require("./compatibility/resolve");
|
|
13
14
|
const project_1 = require("./project");
|
|
14
15
|
const source_1 = require("./compatibility/source");
|
|
@@ -100,12 +101,13 @@ function resolveStaticV2Compatibility(cwd, manifest, source) {
|
|
|
100
101
|
if (parsed.kind !== 'v2')
|
|
101
102
|
throw new Error(`sensor pack "${manifest.pack}" does not provide a v2 compatibility contract`);
|
|
102
103
|
const evidence = (0, discovery_1.discoverProjectEvidence)(cwd, parsed.pack);
|
|
104
|
+
const executionPack = (0, live_1.bindContainedRuntimeCommands)(parsed.pack, evidence.pythonEnvironmentRoot);
|
|
103
105
|
const resolutionEvidence = {
|
|
104
106
|
...evidence,
|
|
105
107
|
...(manifest.packSelection === 'explicit' ? { packSelection: 'explicit' } : {}),
|
|
106
108
|
};
|
|
107
|
-
const initial = (0, resolve_1.resolveProjectCompatibility)(
|
|
108
|
-
return Object.fromEntries(Object.entries(
|
|
109
|
+
const initial = (0, resolve_1.resolveProjectCompatibility)(executionPack, resolutionEvidence).sensors;
|
|
110
|
+
return Object.fromEntries(Object.entries(executionPack.sensors).map(([name, sensor]) => {
|
|
109
111
|
const variant = initial[name]?.variantId === null
|
|
110
112
|
? null
|
|
111
113
|
: sensor.variants.find(candidate => candidate.id === initial[name]?.variantId) ?? null;
|
|
@@ -115,11 +117,11 @@ function resolveStaticV2Compatibility(cwd, manifest, source) {
|
|
|
115
117
|
const probeStatus = variant?.probe.kind === 'config-present'
|
|
116
118
|
? (evidence.configFiles.length > 0 ? 'matched' : 'not-matched')
|
|
117
119
|
: variant?.probe.kind === 'package-script-present'
|
|
118
|
-
? (evidence.scripts.
|
|
120
|
+
? (variant.probe.script !== undefined && evidence.scripts.includes(variant.probe.script) ? 'matched' : 'not-matched')
|
|
119
121
|
: undefined;
|
|
120
122
|
return [name, probeStatus === undefined
|
|
121
123
|
? initial[name]
|
|
122
|
-
: (0, resolve_1.resolveSensorCompatibility)(sensor, { ...resolutionEvidence, probe: { status: probeStatus } }, { pack:
|
|
124
|
+
: (0, resolve_1.resolveSensorCompatibility)(sensor, { ...resolutionEvidence, probe: { status: probeStatus } }, { pack: executionPack.name, sensor: name })];
|
|
123
125
|
}));
|
|
124
126
|
}
|
|
125
127
|
function staticCompatibilityCheck(sensor, live) {
|
|
@@ -51,6 +51,7 @@ async function runSyncCore(options, deps = {}) {
|
|
|
51
51
|
// emission point for this command (mirrors `runInit`/`runUpdateCore`).
|
|
52
52
|
(0, paths_1.noteWindowsCaveat)((m) => console.log(picocolors_1.default.dim(`ℹ ${m}`)));
|
|
53
53
|
const d = { ...defaultDeps, ...deps };
|
|
54
|
+
const reporter = options.reporter ?? (0, bundles_1.createBundleDiagnosticReporter)((message) => process.stderr.write(`${message}\n`));
|
|
54
55
|
const cwd = options.cwd ?? process.cwd();
|
|
55
56
|
const projectRoot = (0, profile_1.findProjectRoot)(cwd);
|
|
56
57
|
if (!projectRoot) {
|
|
@@ -130,7 +131,7 @@ async function runSyncCore(options, deps = {}) {
|
|
|
130
131
|
const method = options.method === 'copy' ? 'copy' : 'symlink';
|
|
131
132
|
let result;
|
|
132
133
|
try {
|
|
133
|
-
result = d.syncProfile({ projectRoot, bundles: (0, bundles_1.discoverAllBundles)(), agents: selectedAgents, method });
|
|
134
|
+
result = d.syncProfile({ projectRoot, bundles: (0, bundles_1.discoverAllBundles)(undefined, reporter), agents: selectedAgents, method });
|
|
134
135
|
}
|
|
135
136
|
catch (e) {
|
|
136
137
|
console.error(picocolors_1.default.red(e.message));
|
|
@@ -29,6 +29,7 @@ const registries_1 = require("../core/registries");
|
|
|
29
29
|
const paths_2 = require("../core/paths");
|
|
30
30
|
const regenerate_1 = require("../core/context/regenerate");
|
|
31
31
|
const reconciliation_1 = require("../core/reconciliation");
|
|
32
|
+
const bundles_1 = require("../core/bundles");
|
|
32
33
|
const install_transaction_1 = require("../core/install-transaction");
|
|
33
34
|
const resync_1 = require("../commands/hooks/resync");
|
|
34
35
|
const defaultDeps = {
|
|
@@ -79,6 +80,7 @@ async function runUpdateCore(options = {}, deps = {}) {
|
|
|
79
80
|
// single emission point for this command (mirrors `runInit`/`runSyncCore`).
|
|
80
81
|
(0, paths_1.noteWindowsCaveat)((m) => console.log(picocolors_1.default.dim(`ℹ ${m}`)));
|
|
81
82
|
const d = { ...defaultDeps, ...deps };
|
|
83
|
+
const reporter = options.reporter ?? (0, bundles_1.createBundleDiagnosticReporter)((message) => process.stderr.write(`${message}\n`));
|
|
82
84
|
const prefs = (0, config_1.getPreferences)();
|
|
83
85
|
const resolved = (0, agent_targets_1.resolveAgentTargetsOrError)({ prefs, explicit: options.agent });
|
|
84
86
|
if (!resolved.ok) {
|
|
@@ -154,7 +156,7 @@ async function runUpdateCore(options = {}, deps = {}) {
|
|
|
154
156
|
console.log(picocolors_1.default.green(` ✓ Regenerated AWM context for: ${refreshed.join(', ')}`));
|
|
155
157
|
let artifactResult;
|
|
156
158
|
try {
|
|
157
|
-
const artifactPlan = d.planReconciliation({ targets: selectedAgents, roots: (0, registries_1.contentRoots)() });
|
|
159
|
+
const artifactPlan = d.planReconciliation({ targets: selectedAgents, roots: (0, registries_1.contentRoots)(), reporter });
|
|
158
160
|
artifactResult = d.applyInstallPlan(artifactPlan);
|
|
159
161
|
}
|
|
160
162
|
catch (e) {
|