agentic-workflow-manager 9.4.1 → 9.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/commands/preflight/checks.js +58 -27
- package/dist/src/commands/sensors/bootstrap.js +171 -0
- package/dist/src/commands/sensors/compatibility/contract.js +18 -15
- package/dist/src/commands/sensors/compatibility/manifest.js +75 -2
- package/dist/src/commands/sensors/compatibility/materialize.js +256 -54
- package/dist/src/commands/sensors/compatibility/pack-source.js +61 -17
- package/dist/src/commands/sensors/compatibility/safe-file.js +286 -0
- package/dist/src/commands/sensors/compatibility/source.js +75 -0
- package/dist/src/commands/sensors/coverage/index.js +2 -2
- package/dist/src/commands/sensors/coverage/resolve.js +35 -29
- package/dist/src/commands/sensors/detection.js +31 -0
- package/dist/src/commands/sensors/exec.js +116 -11
- package/dist/src/commands/sensors/index.js +51 -10
- package/dist/src/commands/sensors/init.js +17 -168
- package/dist/src/commands/sensors/migrate.js +209 -0
- package/dist/src/commands/sensors/project.js +107 -0
- package/dist/src/commands/sensors/run.js +66 -47
- package/dist/src/commands/sensors/status.js +97 -56
- package/dist/src/core/init/steps.js +24 -11
- package/dist/src/core/secure-fs/native-bridge.js +209 -0
- package/dist/tests/commands/preflight/preflight.test.js +31 -0
- package/dist/tests/commands/sensors/bootstrap-observational.test.js +24 -0
- package/dist/tests/commands/sensors/bootstrap.test.js +175 -0
- package/dist/tests/commands/sensors/compatibility/contract.test.js +38 -0
- package/dist/tests/commands/sensors/compatibility/manifest.test.js +90 -0
- package/dist/tests/commands/sensors/compatibility/materialize.test.js +410 -0
- package/dist/tests/commands/sensors/compatibility/pack-source.test.js +249 -0
- package/dist/tests/commands/sensors/compatibility/safe-file.test.js +52 -0
- package/dist/tests/commands/sensors/compatibility/source.test.js +121 -0
- package/dist/tests/commands/sensors/coverage/index.test.js +20 -0
- package/dist/tests/commands/sensors/coverage/resolve.test.js +36 -34
- package/dist/tests/commands/sensors/exec.test.js +86 -0
- package/dist/tests/commands/sensors/index.test.js +54 -0
- package/dist/tests/commands/sensors/init-pack-unavailable.test.js +21 -84
- package/dist/tests/commands/sensors/init.test.js +56 -490
- package/dist/tests/commands/sensors/migrate.test.js +513 -0
- package/dist/tests/commands/sensors/project.test.js +102 -0
- package/dist/tests/commands/sensors/run.test.js +107 -4
- package/dist/tests/commands/sensors/status.test.js +106 -2
- package/dist/tests/core/init/orchestrator.test.js +3 -1
- package/dist/tests/core/secure-fs/native-bridge.test.js +733 -0
- package/dist/tests/integration/preflight-json-pipe.e2e.test.js +5 -0
- package/dist/tests/integration/sensor-bootstrap.e2e.test.js +165 -0
- package/dist/tests/integration/sensor-compatibility.e2e.test.js +14 -23
- package/dist/tests/integration/sensor-coverage.e2e.test.js +15 -24
- package/dist/tests/integration/sensor-structured-run.e2e.test.js +3 -1
- package/dist/tests/structural/native-release-artifacts.test.js +22 -0
- package/dist/tests/structural/release-flags-are-reachable-from-ci.test.js +10 -0
- package/dist/tests/structural/release-registry-provenance.test.js +4 -0
- package/dist/tests/structural/sensor-portability-contract.test.js +44 -0
- package/package.json +6 -2
- package/prebuilds/darwin-arm64/secure_fs.node +0 -0
- package/prebuilds/darwin-x64/secure_fs.node +0 -0
- package/prebuilds/linux-arm64/secure_fs.node +0 -0
- package/prebuilds/linux-x64/secure_fs.node +0 -0
- package/prebuilds/win32-arm64/secure_fs.node +0 -0
- package/prebuilds/win32-x64/secure_fs.node +0 -0
|
@@ -59,6 +59,11 @@ test('preserves actionable no-manifest preflight JSON when verify-sensors is pip
|
|
|
59
59
|
expect(result.status).toBe(1);
|
|
60
60
|
expect(JSON.parse(result.stdout)).toMatchObject({
|
|
61
61
|
status: 'not_configured',
|
|
62
|
+
mode: 'missing',
|
|
63
|
+
reason: 'manifest-absent',
|
|
64
|
+
projectRoot: project,
|
|
65
|
+
manifestPath: path_1.default.join(project, '.awm', 'sensors.json'),
|
|
66
|
+
remedy: expect.any(String),
|
|
62
67
|
checks: expect.arrayContaining([expect.objectContaining({
|
|
63
68
|
id: 'sensors-execution', ok: false,
|
|
64
69
|
detail: 'sensor verdict was not_certified; no sensor established an empirical pass',
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
7
|
+
const child_process_1 = require("child_process");
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const os_1 = __importDefault(require("os"));
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const materialize_1 = require("../../src/commands/sensors/compatibility/materialize");
|
|
12
|
+
const pack_source_1 = require("../../src/commands/sensors/compatibility/pack-source");
|
|
13
|
+
const safe_file_1 = require("../../src/commands/sensors/compatibility/safe-file");
|
|
14
|
+
const native_bridge_1 = require("../../src/core/secure-fs/native-bridge");
|
|
15
|
+
const cliRoot = path_1.default.resolve(__dirname, '../..');
|
|
16
|
+
const bin = path_1.default.join(cliRoot, 'dist/src/index.js');
|
|
17
|
+
const fixtureRoot = path_1.default.join(cliRoot, 'tests/fixtures/sensor-compatibility');
|
|
18
|
+
function hashTree(root) {
|
|
19
|
+
const hash = crypto_1.default.createHash('sha256');
|
|
20
|
+
const walk = (directory) => {
|
|
21
|
+
for (const entry of fs_1.default.readdirSync(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
22
|
+
const file = path_1.default.join(directory, entry.name);
|
|
23
|
+
hash.update(path_1.default.relative(root, file));
|
|
24
|
+
if (entry.isDirectory())
|
|
25
|
+
walk(file);
|
|
26
|
+
else if (entry.isFile())
|
|
27
|
+
hash.update(fs_1.default.readFileSync(file));
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
walk(root);
|
|
31
|
+
return hash.digest('hex');
|
|
32
|
+
}
|
|
33
|
+
function fixture() {
|
|
34
|
+
const root = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'awm-bootstrap-e2e-'));
|
|
35
|
+
const project = path_1.default.join(root, 'project');
|
|
36
|
+
const awmHome = path_1.default.join(root, 'awm-home');
|
|
37
|
+
const registryRoot = path_1.default.join(awmHome, 'registries', 'baseline');
|
|
38
|
+
fs_1.default.cpSync(path_1.default.join(fixtureRoot, 'project'), project, { recursive: true });
|
|
39
|
+
fs_1.default.mkdirSync(path_1.default.join(project, 'node_modules', 'eslint'), { recursive: true });
|
|
40
|
+
fs_1.default.copyFileSync(path_1.default.join(project, 'eslint-tool-package.json'), path_1.default.join(project, 'node_modules', 'eslint', 'package.json'));
|
|
41
|
+
fs_1.default.mkdirSync(path_1.default.dirname(registryRoot), { recursive: true });
|
|
42
|
+
fs_1.default.cpSync(path_1.default.join(fixtureRoot, 'registry'), registryRoot, { recursive: true });
|
|
43
|
+
fs_1.default.writeFileSync(path_1.default.join(awmHome, 'registries.json'), JSON.stringify([{ name: 'baseline', remote: 'fixture' }]));
|
|
44
|
+
return { root, project, awmHome, registryRoot };
|
|
45
|
+
}
|
|
46
|
+
function run(fixture, ...args) {
|
|
47
|
+
return (0, child_process_1.spawnSync)(process.execPath, [bin, 'sensors', 'bootstrap', ...args], {
|
|
48
|
+
cwd: fixture.project,
|
|
49
|
+
encoding: 'utf8',
|
|
50
|
+
env: { ...process.env, AWM_HOME: fixture.awmHome, AWM_NO_UPDATE_CHECK: '1' },
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
function v2Manifest() {
|
|
54
|
+
return {
|
|
55
|
+
schemaVersion: 2,
|
|
56
|
+
pack: 'js-ts',
|
|
57
|
+
sensors: {
|
|
58
|
+
lint: {
|
|
59
|
+
enabled: true, variantId: 'eslint-10',
|
|
60
|
+
command: { executable: 'eslint', resolution: 'node-modules-bin', args: ['.'] },
|
|
61
|
+
assets: ['eslint.fixture.mjs'],
|
|
62
|
+
initializedCompatibility: {
|
|
63
|
+
state: 'certified', reason: 'fixture', variantId: 'eslint-10',
|
|
64
|
+
toolVersion: '10.4.1', runtimeVersion: '24.0.0', certifiedRange: '>=10 <11', evidence: [],
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
beforeAll(() => {
|
|
71
|
+
if (!fs_1.default.existsSync(bin))
|
|
72
|
+
throw new Error(`Sensor bootstrap E2E requires the compiled CLI at ${bin}; run npm run build before this test.`);
|
|
73
|
+
});
|
|
74
|
+
test('compiled bootstrap creates once and the exact second invocation is a byte-stable no-op', () => {
|
|
75
|
+
const subject = fixture();
|
|
76
|
+
try {
|
|
77
|
+
fs_1.default.rmSync(path_1.default.join(subject.project, '.awm'), { recursive: true, force: true });
|
|
78
|
+
const machineBefore = hashTree(subject.awmHome);
|
|
79
|
+
expect(run(subject, '--mode', 'native-gate', '--reason', 'fixture-gate').status).toBe(0);
|
|
80
|
+
const first = hashTree(subject.project);
|
|
81
|
+
expect(run(subject, '--mode', 'native-gate', '--reason', 'fixture-gate').status).toBe(0);
|
|
82
|
+
expect(hashTree(subject.project)).toBe(first);
|
|
83
|
+
expect(hashTree(subject.awmHome)).toBe(machineBefore);
|
|
84
|
+
expect(JSON.parse(fs_1.default.readFileSync(path_1.default.join(subject.project, '.awm', 'sensors.json'), 'utf8'))).toEqual({ schemaVersion: 3, mode: 'native-gate', reason: 'fixture-gate' });
|
|
85
|
+
}
|
|
86
|
+
finally {
|
|
87
|
+
fs_1.default.rmSync(subject.root, { recursive: true, force: true });
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
test('compiled project-sensors bootstrap materializes only selected assets and then no-ops', () => {
|
|
91
|
+
const subject = fixture();
|
|
92
|
+
try {
|
|
93
|
+
fs_1.default.rmSync(path_1.default.join(subject.project, '.awm'), { recursive: true, force: true });
|
|
94
|
+
fs_1.default.copyFileSync(path_1.default.join(subject.registryRoot, 'sensor-packs', 'js-ts', 'eslint.fixture.mjs'), path_1.default.join(subject.project, 'eslint.fixture.mjs'));
|
|
95
|
+
const machineBefore = hashTree(subject.awmHome);
|
|
96
|
+
const result = run(subject, '--mode', 'project-sensors');
|
|
97
|
+
if (result.status !== 0)
|
|
98
|
+
throw new Error(`project-sensors bootstrap failed: ${result.stdout}\n${result.stderr}`);
|
|
99
|
+
expect(`${result.stdout}${result.stderr}`).toContain('pack=js-ts');
|
|
100
|
+
expect(fs_1.default.existsSync(path_1.default.join(subject.project, 'eslint.fixture.mjs'))).toBe(true);
|
|
101
|
+
expect(fs_1.default.existsSync(path_1.default.join(subject.project, 'pack.json'))).toBe(false);
|
|
102
|
+
const first = hashTree(subject.project);
|
|
103
|
+
expect(run(subject, '--mode', 'project-sensors').status).toBe(0);
|
|
104
|
+
expect(hashTree(subject.project)).toBe(first);
|
|
105
|
+
expect(hashTree(subject.awmHome)).toBe(machineBefore);
|
|
106
|
+
}
|
|
107
|
+
finally {
|
|
108
|
+
fs_1.default.rmSync(subject.root, { recursive: true, force: true });
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
test('injected manifest publication failure compensates created assets without changing unrelated project or machine bytes', () => {
|
|
112
|
+
const subject = fixture();
|
|
113
|
+
try {
|
|
114
|
+
fs_1.default.rmSync(path_1.default.join(subject.project, '.awm'), { recursive: true, force: true });
|
|
115
|
+
const beforePackage = fs_1.default.readFileSync(path_1.default.join(subject.project, 'package.json'));
|
|
116
|
+
const beforeConfig = fs_1.default.readFileSync(path_1.default.join(subject.project, 'eslint.config.mjs'));
|
|
117
|
+
const beforeMachine = hashTree(subject.awmHome);
|
|
118
|
+
const source = (0, pack_source_1.resolvePackSource)('js-ts', { registries: [{ name: 'baseline', remote: 'fixture', contentRoot: subject.registryRoot }] });
|
|
119
|
+
(0, safe_file_1.setSecureFsForTests)({
|
|
120
|
+
withProjectLease: native_bridge_1.secureFs.withProjectLease,
|
|
121
|
+
readRegularFile: native_bridge_1.secureFs.readRegularFile,
|
|
122
|
+
writeProjectTransaction: ((root, destination, content, options) => {
|
|
123
|
+
if (destination === '.awm/sensors.json')
|
|
124
|
+
throw new Error('injected rename failure');
|
|
125
|
+
native_bridge_1.secureFs.writeProjectTransaction(root, destination, content, options);
|
|
126
|
+
}),
|
|
127
|
+
removeObservedProjectFile: native_bridge_1.secureFs.removeObservedProjectFile,
|
|
128
|
+
});
|
|
129
|
+
expect(() => (0, materialize_1.materializePortableSensors)({
|
|
130
|
+
projectRoot: subject.project, pack: 'js-ts', source,
|
|
131
|
+
sensors: { lint: { enabled: true, variantId: 'eslint-10', command: { executable: 'eslint', resolution: 'node-modules-bin', args: ['.'] }, assets: ['eslint.fixture.mjs'], initializedCompatibility: { state: 'certified', reason: 'fixture', variantId: 'eslint-10', toolVersion: '10.4.1', runtimeVersion: '24.0.0', certifiedRange: '>=10 <11', evidence: [] } } },
|
|
132
|
+
})).toThrow('injected rename failure');
|
|
133
|
+
expect(fs_1.default.readFileSync(path_1.default.join(subject.project, 'package.json'))).toEqual(beforePackage);
|
|
134
|
+
expect(fs_1.default.readFileSync(path_1.default.join(subject.project, 'eslint.config.mjs'))).toEqual(beforeConfig);
|
|
135
|
+
expect(fs_1.default.existsSync(path_1.default.join(subject.project, 'eslint.fixture.mjs'))).toBe(false);
|
|
136
|
+
expect(fs_1.default.existsSync(path_1.default.join(subject.project, '.awm', 'sensors.json'))).toBe(false);
|
|
137
|
+
expect(hashTree(subject.awmHome)).toBe(beforeMachine);
|
|
138
|
+
expect(fs_1.default.existsSync(path_1.default.join(subject.project, '.awm', 'sensors.json.tmp'))).toBe(false);
|
|
139
|
+
}
|
|
140
|
+
finally {
|
|
141
|
+
(0, safe_file_1.setSecureFsForTests)(undefined);
|
|
142
|
+
fs_1.default.rmSync(subject.root, { recursive: true, force: true });
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
test('compiled bootstrap migrates v2 once, preserves all non-manifest project bytes, and then no-ops', () => {
|
|
146
|
+
const subject = fixture();
|
|
147
|
+
try {
|
|
148
|
+
const manifest = path_1.default.join(subject.project, '.awm', 'sensors.json');
|
|
149
|
+
fs_1.default.writeFileSync(manifest, JSON.stringify(v2Manifest(), null, 2) + '\n');
|
|
150
|
+
const machineBefore = hashTree(subject.awmHome);
|
|
151
|
+
const assetBefore = fs_1.default.readFileSync(path_1.default.join(subject.project, 'eslint.config.mjs'));
|
|
152
|
+
expect(run(subject).status).toBe(0);
|
|
153
|
+
const first = hashTree(subject.project);
|
|
154
|
+
const migrated = JSON.parse(fs_1.default.readFileSync(manifest, 'utf8'));
|
|
155
|
+
expect(migrated).toMatchObject({ schemaVersion: 3, mode: 'project-sensors', source: { registry: 'baseline' } });
|
|
156
|
+
expect(migrated).not.toHaveProperty('registryRoot');
|
|
157
|
+
expect(fs_1.default.readFileSync(path_1.default.join(subject.project, 'eslint.config.mjs'))).toEqual(assetBefore);
|
|
158
|
+
expect(run(subject).status).toBe(0);
|
|
159
|
+
expect(hashTree(subject.project)).toBe(first);
|
|
160
|
+
expect(hashTree(subject.awmHome)).toBe(machineBefore);
|
|
161
|
+
}
|
|
162
|
+
finally {
|
|
163
|
+
fs_1.default.rmSync(subject.root, { recursive: true, force: true });
|
|
164
|
+
}
|
|
165
|
+
});
|
|
@@ -17,12 +17,6 @@ const bin = path_1.default.join(cliDir, 'dist/src/index.js');
|
|
|
17
17
|
const fixtureRoot = path_1.default.join(cliDir, 'tests/fixtures/sensor-compatibility');
|
|
18
18
|
const projectFixture = path_1.default.join(fixtureRoot, 'project');
|
|
19
19
|
const registryFixture = path_1.default.join(fixtureRoot, 'registry');
|
|
20
|
-
const noFollowUnavailableOnNativeWindows = process.platform === 'win32'
|
|
21
|
-
&& typeof fs_1.default.constants.O_NOFOLLOW !== 'number';
|
|
22
|
-
// Other native platforms exercise coverage through testWithNoFollow; this
|
|
23
|
-
// branch preserves the deliberate Windows fail-closed behavior instead.
|
|
24
|
-
const testWithNoFollow = noFollowUnavailableOnNativeWindows ? test.skip : test;
|
|
25
|
-
const testWithoutNoFollowOnNativeWindows = noFollowUnavailableOnNativeWindows ? test : test.skip;
|
|
26
20
|
function hashTree(root) {
|
|
27
21
|
const hash = crypto_1.default.createHash('sha256');
|
|
28
22
|
const walk = (directory) => {
|
|
@@ -48,6 +42,9 @@ function createFixture() {
|
|
|
48
42
|
fs_1.default.copyFileSync(path_1.default.join(project, 'eslint-tool-package.json'), path_1.default.join(project, 'node_modules', 'eslint', 'package.json'));
|
|
49
43
|
fs_1.default.mkdirSync(path_1.default.dirname(registryRoot), { recursive: true });
|
|
50
44
|
fs_1.default.cpSync(registryFixture, registryRoot, { recursive: true });
|
|
45
|
+
// The fixture pack's certified probe requires this selected config to be
|
|
46
|
+
// present before bootstrap can prove compatibility and publish v3.
|
|
47
|
+
fs_1.default.copyFileSync(path_1.default.join(registryRoot, 'sensor-packs', 'js-ts', 'eslint.fixture.mjs'), path_1.default.join(project, 'eslint.fixture.mjs'));
|
|
51
48
|
fs_1.default.writeFileSync(path_1.default.join(awmHome, 'registries.json'), JSON.stringify([{ name: 'baseline', remote: 'fixture' }]));
|
|
52
49
|
return { root, project, awmHome, registryRoot };
|
|
53
50
|
}
|
|
@@ -66,7 +63,8 @@ function runAwm(fixture, ...args) {
|
|
|
66
63
|
});
|
|
67
64
|
}
|
|
68
65
|
function json(result) {
|
|
69
|
-
|
|
66
|
+
if (result.status !== 0)
|
|
67
|
+
throw new Error(`CLI failed: ${result.stdout ?? ''}\n${result.stderr ?? ''}`);
|
|
70
68
|
if (!(result.stdout ?? '').trim())
|
|
71
69
|
throw new Error(`coverage emitted no JSON: stderr=${result.stderr ?? ''}`);
|
|
72
70
|
return JSON.parse(result.stdout ?? '');
|
|
@@ -97,7 +95,7 @@ test.each(['linux', 'darwin', 'win32'])('keeps injected resolver semantics consi
|
|
|
97
95
|
fs_1.default.rmSync(fixture.root, { recursive: true, force: true });
|
|
98
96
|
}
|
|
99
97
|
});
|
|
100
|
-
|
|
98
|
+
test('compiled binary dispatches coverage and emits parseable JSON on the native CI platform', () => {
|
|
101
99
|
const fixture = createFixture();
|
|
102
100
|
try {
|
|
103
101
|
const report = json(runCli(fixture, 'coverage', '--json'));
|
|
@@ -107,7 +105,7 @@ testWithNoFollow('compiled binary dispatches coverage and emits parseable JSON o
|
|
|
107
105
|
fs_1.default.rmSync(fixture.root, { recursive: true, force: true });
|
|
108
106
|
}
|
|
109
107
|
});
|
|
110
|
-
|
|
108
|
+
test('compiled sensors run returns nonzero while preserving parseable not_certified JSON', () => {
|
|
111
109
|
const fixture = createFixture();
|
|
112
110
|
try {
|
|
113
111
|
fs_1.default.rmSync(path_1.default.join(fixture.project, '.awm', 'sensors.json'));
|
|
@@ -119,9 +117,10 @@ testWithNoFollow('compiled sensors run returns nonzero while preserving parseabl
|
|
|
119
117
|
fs_1.default.rmSync(fixture.root, { recursive: true, force: true });
|
|
120
118
|
}
|
|
121
119
|
});
|
|
122
|
-
|
|
120
|
+
test('compiled status reports static READY without writing or executing the project sensor (R6, R6.2)', () => {
|
|
123
121
|
const fixture = createFixture();
|
|
124
122
|
try {
|
|
123
|
+
fs_1.default.rmSync(path_1.default.join(fixture.project, '.awm', 'sensors.json'));
|
|
125
124
|
const localBin = path_1.default.join(fixture.project, 'node_modules', '.bin', 'eslint');
|
|
126
125
|
fs_1.default.mkdirSync(path_1.default.dirname(localBin), { recursive: true });
|
|
127
126
|
fs_1.default.writeFileSync(localBin, 'this fixture must never be executed by status\n');
|
|
@@ -139,7 +138,7 @@ testWithNoFollow('compiled status reports static READY without writing or execut
|
|
|
139
138
|
fs_1.default.rmSync(fixture.root, { recursive: true, force: true });
|
|
140
139
|
}
|
|
141
140
|
});
|
|
142
|
-
|
|
141
|
+
test('legacy coverage stays unverified, init migrates explicitly, and version drift is visible (R7.2, R7.8)', () => {
|
|
143
142
|
const fixture = createFixture();
|
|
144
143
|
try {
|
|
145
144
|
const before = [hashTree(fixture.project), hashTree(fixture.awmHome)];
|
|
@@ -149,10 +148,13 @@ testWithNoFollow('legacy coverage stays unverified, init migrates explicitly, an
|
|
|
149
148
|
id: 'lint-errors', status: 'unverifiable', detectors: expect.arrayContaining([expect.objectContaining({ compatibility: expect.objectContaining({ state: 'compatible-unverified' }) })]),
|
|
150
149
|
})]));
|
|
151
150
|
expect([hashTree(fixture.project), hashTree(fixture.awmHome)]).toEqual(before);
|
|
151
|
+
// v1 declarations are intentionally preserved; creation is explicitly
|
|
152
|
+
// restarted once the owner removes the legacy declaration.
|
|
153
|
+
fs_1.default.rmSync(path_1.default.join(fixture.project, '.awm', 'sensors.json'));
|
|
152
154
|
const migrated = runCli(fixture, 'init', '--registry-root', fixture.registryRoot, '--pack', 'js-ts', '--no-configure');
|
|
153
155
|
expect(migrated.status).toBe(0);
|
|
154
156
|
const manifest = JSON.parse(fs_1.default.readFileSync(path_1.default.join(fixture.project, '.awm', 'sensors.json'), 'utf8'));
|
|
155
|
-
expect(manifest).toMatchObject({ schemaVersion:
|
|
157
|
+
expect(manifest).toMatchObject({ schemaVersion: 3, mode: 'project-sensors', pack: 'js-ts', source: { registry: 'baseline' }, sensors: { lint: { variantId: 'eslint-10' } } });
|
|
156
158
|
const certified = json(runCli(fixture, 'coverage', '--json'));
|
|
157
159
|
expect(certified.static.classes).toEqual(expect.arrayContaining([expect.objectContaining({
|
|
158
160
|
id: 'lint-errors', status: 'covered', detectors: expect.arrayContaining([expect.objectContaining({ compatibility: expect.objectContaining({ state: 'certified' }) })]),
|
|
@@ -167,14 +169,3 @@ testWithNoFollow('legacy coverage stays unverified, init migrates explicitly, an
|
|
|
167
169
|
fs_1.default.rmSync(fixture.root, { recursive: true, force: true });
|
|
168
170
|
}
|
|
169
171
|
});
|
|
170
|
-
testWithoutNoFollowOnNativeWindows('coverage fails closed when native Windows cannot prevent symlink dereference', () => {
|
|
171
|
-
const fixture = createFixture();
|
|
172
|
-
try {
|
|
173
|
-
const result = runCli(fixture, 'coverage', '--json');
|
|
174
|
-
expect(result.status).toBe(1);
|
|
175
|
-
expect(`${result.stdout ?? ''}${result.stderr ?? ''}`).toContain('platform cannot guarantee no symlink dereference');
|
|
176
|
-
}
|
|
177
|
-
finally {
|
|
178
|
-
fs_1.default.rmSync(fixture.root, { recursive: true, force: true });
|
|
179
|
-
}
|
|
180
|
-
});
|
|
@@ -12,9 +12,6 @@ const cliDir = path_1.default.resolve(__dirname, '../..');
|
|
|
12
12
|
const bin = path_1.default.join(cliDir, 'dist/src/index.js');
|
|
13
13
|
const fixture = path_1.default.join(cliDir, 'tests/fixtures/sensor-coverage/js-ts-gap');
|
|
14
14
|
const registryFixture = path_1.default.join(cliDir, 'tests/fixtures/sensor-coverage/registry');
|
|
15
|
-
const noFollowUnavailableOnNativeWindows = process.platform === 'win32'
|
|
16
|
-
&& typeof fs_1.default.constants.O_NOFOLLOW !== 'number';
|
|
17
|
-
const testWithNoFollow = noFollowUnavailableOnNativeWindows ? test.skip : test;
|
|
18
15
|
const hashTree = (root) => {
|
|
19
16
|
const hash = crypto_1.default.createHash('sha256');
|
|
20
17
|
const walk = (directory) => {
|
|
@@ -71,32 +68,26 @@ test('compiled CLI reports static and read-only empirical coverage without leaki
|
|
|
71
68
|
const result = (0, child_process_1.spawnSync)(process.execPath, [bin, 'sensors', 'coverage', '--json'], {
|
|
72
69
|
cwd: project, encoding: 'utf8', env: { ...process.env, AWM_HOME: awmHome, AWM_NO_UPDATE_CHECK: '1' },
|
|
73
70
|
});
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
unclassified: { occurrences: 1 },
|
|
88
|
-
} });
|
|
89
|
-
expect(result.stdout).not.toContain('fixture-secret-description');
|
|
90
|
-
expect(result.stdout).not.toContain('private-lint-signature');
|
|
91
|
-
expect(result.stdout).not.toContain('windows secret');
|
|
92
|
-
}
|
|
71
|
+
expect(result.status).toBe(0);
|
|
72
|
+
const report = JSON.parse(result.stdout ?? '');
|
|
73
|
+
expect(report.static.classes).toEqual(expect.arrayContaining([
|
|
74
|
+
expect.objectContaining({ id: 'formatting', status: 'unverifiable' }),
|
|
75
|
+
expect.objectContaining({ id: 'project-style-conventions', status: 'unverifiable' }),
|
|
76
|
+
]));
|
|
77
|
+
expect(report).toMatchObject({ schemaVersion: 2, empirical: {
|
|
78
|
+
status: 'partial', classes: [expect.objectContaining({ defectClass: 'lint-errors', occurrences: 2, recurrent: true })],
|
|
79
|
+
unclassified: { occurrences: 1 },
|
|
80
|
+
} });
|
|
81
|
+
expect(result.stdout).not.toContain('fixture-secret-description');
|
|
82
|
+
expect(result.stdout).not.toContain('private-lint-signature');
|
|
83
|
+
expect(result.stdout).not.toContain('windows secret');
|
|
93
84
|
expect([hashTree(project), hashTree(awmHome)]).toEqual(before);
|
|
94
85
|
}
|
|
95
86
|
finally {
|
|
96
87
|
fs_1.default.rmSync(tmp, { recursive: true, force: true });
|
|
97
88
|
}
|
|
98
89
|
});
|
|
99
|
-
|
|
90
|
+
test('compiled coverage keeps static verdict stable while --min changes only recurrence emphasis', () => {
|
|
100
91
|
const { tmp, project, awmHome } = runWithFixture();
|
|
101
92
|
try {
|
|
102
93
|
const before = [hashTree(project), hashTree(awmHome)];
|
|
@@ -115,7 +106,7 @@ testWithNoFollow('compiled coverage keeps static verdict stable while --min chan
|
|
|
115
106
|
fs_1.default.rmSync(tmp, { recursive: true, force: true });
|
|
116
107
|
}
|
|
117
108
|
});
|
|
118
|
-
|
|
109
|
+
test('informative states exit zero; malformed contract exits non-zero (R2.7, R2.9)', () => {
|
|
119
110
|
const run = (mutatePack) => {
|
|
120
111
|
const { tmp, project, awmHome } = runWithFixture(mutatePack);
|
|
121
112
|
try {
|
|
@@ -107,8 +107,10 @@ test('compiled sensors run materializes a v2 registry command and passes its lit
|
|
|
107
107
|
assertSuccessfulCli(initialized, 'init');
|
|
108
108
|
const manifest = JSON.parse(fs_1.default.readFileSync(path_1.default.join(fixture.project, '.awm', 'sensors.json'), 'utf8'));
|
|
109
109
|
expect(manifest).toMatchObject({
|
|
110
|
-
schemaVersion:
|
|
110
|
+
schemaVersion: 3,
|
|
111
|
+
mode: 'project-sensors',
|
|
111
112
|
pack: 'fixture',
|
|
113
|
+
source: { registry: 'baseline' },
|
|
112
114
|
sensors: { structured: { variantId: 'fixture-v1', command: { executable: 'node', resolution: 'path', args: ['fixture-sensor.mjs', fixture.literal] } } },
|
|
113
115
|
});
|
|
114
116
|
const result = runCli(fixture, 'run', '--fast');
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const child_process_1 = require("child_process");
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const CLI_ROOT = path_1.default.resolve(__dirname, '..', '..');
|
|
10
|
+
const REPO_ROOT = path_1.default.resolve(CLI_ROOT, '..');
|
|
11
|
+
describe('native release artifacts', () => {
|
|
12
|
+
it('keeps native prebuilds generated instead of versioned source', () => {
|
|
13
|
+
const gitignore = fs_1.default.readFileSync(path_1.default.join(REPO_ROOT, '.gitignore'), 'utf8');
|
|
14
|
+
const tracked = (0, child_process_1.execFileSync)('git', ['ls-files', '--', 'cli/prebuilds'], {
|
|
15
|
+
cwd: REPO_ROOT,
|
|
16
|
+
encoding: 'utf8',
|
|
17
|
+
}).trim();
|
|
18
|
+
expect(gitignore).toContain('cli/prebuilds/');
|
|
19
|
+
expect(gitignore).toContain('cli/native-artifacts/');
|
|
20
|
+
expect(tracked).toBe('');
|
|
21
|
+
});
|
|
22
|
+
});
|
|
@@ -24,6 +24,7 @@ const CLI_ROOT = path_1.default.resolve(__dirname, '..', '..');
|
|
|
24
24
|
const REPO_ROOT = path_1.default.resolve(CLI_ROOT, '..');
|
|
25
25
|
const RELEASE_ENTRY = path_1.default.join(CLI_ROOT, 'src', 'release', 'index.ts');
|
|
26
26
|
const RELEASE_WORKFLOW = path_1.default.join(REPO_ROOT, '.github', 'workflows', 'release.yml');
|
|
27
|
+
const CI_WORKFLOW = path_1.default.join(REPO_ROOT, '.github', 'workflows', 'ci.yml');
|
|
27
28
|
// Flags deliberadamente NO expuestos a CI, con la razón por la que no lo están. Sacar uno
|
|
28
29
|
// de acá sin exponerlo en el workflow rompe el test a propósito.
|
|
29
30
|
const NOT_FOR_CI = {
|
|
@@ -65,3 +66,12 @@ describe('flags del release: implementados <=> alcanzables desde CI', () => {
|
|
|
65
66
|
expect(workflowSource).toMatch(/--force \{0\}/);
|
|
66
67
|
});
|
|
67
68
|
});
|
|
69
|
+
describe('matriz de compilacion nativa Windows', () => {
|
|
70
|
+
for (const workflowPath of [CI_WORKFLOW, RELEASE_WORKFLOW]) {
|
|
71
|
+
it(`${path_1.default.basename(workflowPath)} fija x64 en Windows Server 2022 y conserva ARM64 nativo`, () => {
|
|
72
|
+
const source = fs_1.default.readFileSync(workflowPath, 'utf8');
|
|
73
|
+
expect(source).toMatch(/- os: windows-2022\s+target: win32-x64/);
|
|
74
|
+
expect(source).toMatch(/- os: windows-11-arm\s+target: win32-arm64/);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
});
|
|
@@ -18,4 +18,8 @@ describe('release registry provenance', () => {
|
|
|
18
18
|
.toBeLessThan(workflow.indexOf('- name: Release'));
|
|
19
19
|
expect(workflow).toMatch(/REGISTRY_TAG: v3\.4\.0[\s\S]*AWM_PUBLISHED_REGISTRY_TAG: v3\.4\.0/);
|
|
20
20
|
});
|
|
21
|
+
it('normalizes npm pack JSON from both array and object output formats', () => {
|
|
22
|
+
expect(workflow).toContain('Array.isArray(packed) ? packed[0] : Object.values(packed)[0]');
|
|
23
|
+
expect(workflow).toContain('npm pack returned no tarball filename');
|
|
24
|
+
});
|
|
21
25
|
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const fs_1 = __importDefault(require("fs"));
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const commander_1 = require("commander");
|
|
9
|
+
jest.mock('@clack/prompts', () => ({ log: { error: jest.fn(), success: jest.fn(), info: jest.fn() } }));
|
|
10
|
+
jest.mock('picocolors', () => ({ green: (value) => value, yellow: (value) => value, red: (value) => value }));
|
|
11
|
+
const sensors_1 = require("../../src/commands/sensors");
|
|
12
|
+
const manifest_1 = require("../../src/commands/sensors/compatibility/manifest");
|
|
13
|
+
const root = path_1.default.resolve(__dirname, '../../..');
|
|
14
|
+
function bootstrapHelp() {
|
|
15
|
+
const program = new commander_1.Command().name('awm');
|
|
16
|
+
(0, sensors_1.registerSensorsCommand)(program);
|
|
17
|
+
const sensors = program.commands.find(command => command.name() === 'sensors');
|
|
18
|
+
return sensors.commands.find(command => command.name() === 'bootstrap').helpInformation();
|
|
19
|
+
}
|
|
20
|
+
describe('Publication B portable bootstrap contract', () => {
|
|
21
|
+
it('exposes the exact explicit bootstrap syntax', () => {
|
|
22
|
+
const help = bootstrapHelp();
|
|
23
|
+
expect(help).toContain('--mode <mode>');
|
|
24
|
+
expect(help).toContain('--reason <text>');
|
|
25
|
+
expect(help).toContain('--dry-run');
|
|
26
|
+
});
|
|
27
|
+
it('documents schema v3, explicit migration, dry-run, and environment separation', () => {
|
|
28
|
+
const reference = fs_1.default.readFileSync(path_1.default.join(root, 'docs/cli-reference.md'), 'utf8');
|
|
29
|
+
expect(reference).toContain('awm sensors bootstrap [--mode project-sensors|native-gate|opt-out] [--reason <text>] [--dry-run]');
|
|
30
|
+
expect(reference).toContain('schemaVersion: 3');
|
|
31
|
+
expect(reference).toContain('never writes');
|
|
32
|
+
expect(reference).toContain('each environment updates its own AWM installation separately');
|
|
33
|
+
});
|
|
34
|
+
it('keeps all documented v3 declarations free of machine registry paths', () => {
|
|
35
|
+
const reference = fs_1.default.readFileSync(path_1.default.join(root, 'docs/cli-reference.md'), 'utf8');
|
|
36
|
+
const examples = Array.from(reference.matchAll(/```json\s*\n([\s\S]*?)```/g), match => JSON.parse(match[1]));
|
|
37
|
+
const v3 = examples.filter(example => example?.schemaVersion === 3);
|
|
38
|
+
expect(v3.length).toBeGreaterThan(0);
|
|
39
|
+
v3.forEach(example => {
|
|
40
|
+
expect(() => (0, manifest_1.parseSensorManifest)(example, 'documented v3 declaration')).not.toThrow();
|
|
41
|
+
expect(JSON.stringify(example)).not.toMatch(/registryRoot|[A-Za-z]:\\\\|\/(?:Users|home|srv)\//);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
});
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentic-workflow-manager",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.6.0",
|
|
4
4
|
"main": "dist/src/index.js",
|
|
5
5
|
"bin": {
|
|
6
6
|
"awm": "./dist/src/index.js"
|
|
7
7
|
},
|
|
8
8
|
"files": [
|
|
9
|
-
"dist"
|
|
9
|
+
"dist",
|
|
10
|
+
"prebuilds"
|
|
10
11
|
],
|
|
11
12
|
"repository": {
|
|
12
13
|
"type": "git",
|
|
@@ -17,6 +18,8 @@
|
|
|
17
18
|
"doc": "docs"
|
|
18
19
|
},
|
|
19
20
|
"scripts": {
|
|
21
|
+
"native:build": "node-gyp rebuild --directory native && node scripts/package-native.js",
|
|
22
|
+
"native:package-check": "node scripts/assert-native-package.js",
|
|
20
23
|
"build": "tsc && chmod +x dist/src/index.js",
|
|
21
24
|
"typecheck": "tsc --noEmit",
|
|
22
25
|
"lint": "eslint . --config eslint.config.awm.mjs --cache --format json",
|
|
@@ -59,6 +62,7 @@
|
|
|
59
62
|
"eslint": "10.8.1",
|
|
60
63
|
"jest": "^30.2.0",
|
|
61
64
|
"js-yaml": "4.1.0",
|
|
65
|
+
"node-gyp": "^11.5.0",
|
|
62
66
|
"ts-jest": "^29.4.6",
|
|
63
67
|
"ts-node": "^10.9.2",
|
|
64
68
|
"typescript": "^5.9.3"
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|