agentic-workflow-manager 9.5.0 → 9.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/commands/context/index.js +3 -1
- package/dist/src/commands/preflight/checks.js +2 -2
- 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 +29 -1
- package/dist/src/commands/sensors/compatibility/materialize.js +256 -54
- package/dist/src/commands/sensors/compatibility/safe-file.js +240 -5
- package/dist/src/commands/sensors/coverage/index.js +2 -2
- package/dist/src/commands/sensors/coverage/resolve.js +20 -10
- 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 -170
- package/dist/src/commands/sensors/migrate.js +209 -0
- package/dist/src/commands/sensors/run.js +2 -2
- package/dist/src/core/discovery.js +79 -4
- package/dist/src/core/init/steps.js +24 -11
- package/dist/src/core/orchestrators.js +34 -10
- package/dist/src/core/secure-fs/native-bridge.js +209 -0
- package/dist/src/core/text.js +7 -0
- package/dist/tests/commands/context.test.js +9 -1
- package/dist/tests/commands/hooks/install.test.js +4 -0
- package/dist/tests/commands/hooks/resync.test.js +2 -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 +67 -0
- package/dist/tests/commands/sensors/compatibility/materialize.test.js +410 -0
- package/dist/tests/commands/sensors/compatibility/pack-source.test.js +34 -1
- package/dist/tests/commands/sensors/compatibility/safe-file.test.js +52 -0
- package/dist/tests/commands/sensors/coverage/index.test.js +20 -0
- package/dist/tests/commands/sensors/coverage/resolve.test.js +10 -0
- 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/core/context/orchestrator.test.js +2 -0
- package/dist/tests/core/discovery-multiroot.test.js +5 -0
- package/dist/tests/core/discovery-parent-race.test.js +95 -0
- package/dist/tests/core/discovery-safe-read.test.js +115 -0
- package/dist/tests/core/discovery.test.js +28 -1
- package/dist/tests/core/init/orchestrator.test.js +3 -1
- package/dist/tests/core/orchestrators.test.js +171 -14
- package/dist/tests/core/secure-fs/native-bridge.test.js +733 -0
- package/dist/tests/core/text.test.js +10 -0
- package/dist/tests/integration/context-orchestrators-e2e.test.js +57 -7
- package/dist/tests/integration/sensor-bootstrap.e2e.test.js +165 -0
- package/dist/tests/integration/sensor-compatibility.e2e.test.js +10 -2
- 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
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const text_1 = require("../../src/core/text");
|
|
4
|
+
describe('sanitizeDiagnosticText', () => {
|
|
5
|
+
it.each([
|
|
6
|
+
['a\r\nb\rc\nd\te', 'a b c d e'],
|
|
7
|
+
['a\x00\x07\x1b[31m\x7f\x85\x9bz', 'a[31mz'],
|
|
8
|
+
['a\u2028b\u2029c', 'a b c'],
|
|
9
|
+
['plain *markdown* — café', 'plain *markdown* — café'],
|
|
10
|
+
])('sanitizes a single diagnostic field: %j', (input, expected) => {
|
|
11
|
+
expect((0, text_1.sanitizeDiagnosticText)(input)).toBe(expected);
|
|
12
|
+
});
|
|
13
|
+
});
|
|
4
14
|
describe('stripControlChars', () => {
|
|
5
15
|
it('elimina ESC y demas bytes de control C0', () => {
|
|
6
16
|
expect((0, text_1.stripControlChars)('a\x1b[31mb\x00c\x07d')).toBe('a[31mbcd');
|
|
@@ -14,13 +14,13 @@ function seedRegistry() {
|
|
|
14
14
|
const home = fs_1.default.realpathSync(fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'awm-ctx-e2e-')));
|
|
15
15
|
const awmHome = path_1.default.join(home, '.awm');
|
|
16
16
|
const root = path_1.default.join(awmHome, 'registries', 'test-registry');
|
|
17
|
-
fs_1.default.mkdirSync(path_1.default.join(root, 'skills', '
|
|
17
|
+
fs_1.default.mkdirSync(path_1.default.join(root, 'skills', 'ejemplo-proceso'), { recursive: true });
|
|
18
18
|
fs_1.default.writeFileSync(path_1.default.join(awmHome, 'registries.json'), JSON.stringify([{ name: 'test-registry', remote: 'https://example.invalid/test.git' }]));
|
|
19
19
|
fs_1.default.writeFileSync(path_1.default.join(root, 'awm-registry.json'), JSON.stringify({
|
|
20
20
|
minCliVersion: '8.5.0',
|
|
21
21
|
orchestrator: { name: 'ejemplo-proceso', appliesWhen: 'cuando hay una tarea sin plan', terminatesTo: 'development-process' },
|
|
22
22
|
}));
|
|
23
|
-
fs_1.default.writeFileSync(path_1.default.join(root, 'skills', '
|
|
23
|
+
fs_1.default.writeFileSync(path_1.default.join(root, 'skills', 'ejemplo-proceso', 'SKILL.md'), '---\nname: ejemplo-proceso\nversion: "1.0.0"\n---\n\n# ejemplo-proceso\n');
|
|
24
24
|
return home;
|
|
25
25
|
}
|
|
26
26
|
/**
|
|
@@ -36,7 +36,7 @@ function seedRealisticRegistry() {
|
|
|
36
36
|
const home = fs_1.default.realpathSync(fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'awm-ctx-e2e-real-')));
|
|
37
37
|
const awmHome = path_1.default.join(home, '.awm');
|
|
38
38
|
const root = path_1.default.join(awmHome, 'registries', 'test-registry');
|
|
39
|
-
fs_1.default.mkdirSync(path_1.default.join(root, 'skills', '
|
|
39
|
+
fs_1.default.mkdirSync(path_1.default.join(root, 'skills', 'task_capture_process'), { recursive: true });
|
|
40
40
|
fs_1.default.writeFileSync(path_1.default.join(awmHome, 'registries.json'), JSON.stringify([{ name: 'test-registry', remote: 'https://example.invalid/test.git' }]));
|
|
41
41
|
fs_1.default.writeFileSync(path_1.default.join(root, 'awm-registry.json'), JSON.stringify({
|
|
42
42
|
minCliVersion: '8.5.0',
|
|
@@ -46,7 +46,7 @@ function seedRealisticRegistry() {
|
|
|
46
46
|
terminatesTo: 'development-process',
|
|
47
47
|
},
|
|
48
48
|
}));
|
|
49
|
-
fs_1.default.writeFileSync(path_1.default.join(root, 'skills', '
|
|
49
|
+
fs_1.default.writeFileSync(path_1.default.join(root, 'skills', 'task_capture_process', 'SKILL.md'), '---\nname: task_capture_process\nversion: "1.0.0"\n---\n\n# task_capture_process\n');
|
|
50
50
|
return home;
|
|
51
51
|
}
|
|
52
52
|
/**
|
|
@@ -63,7 +63,7 @@ function seedMixedRegistries() {
|
|
|
63
63
|
const awmHome = path_1.default.join(home, '.awm');
|
|
64
64
|
const sanoRoot = path_1.default.join(awmHome, 'registries', 'sano-registry');
|
|
65
65
|
const rotoRoot = path_1.default.join(awmHome, 'registries', 'roto-registry');
|
|
66
|
-
fs_1.default.mkdirSync(path_1.default.join(sanoRoot, 'skills', '
|
|
66
|
+
fs_1.default.mkdirSync(path_1.default.join(sanoRoot, 'skills', 'proceso-sano'), { recursive: true });
|
|
67
67
|
fs_1.default.mkdirSync(path_1.default.join(rotoRoot, 'skills', 'using-awm'), { recursive: true });
|
|
68
68
|
fs_1.default.writeFileSync(path_1.default.join(awmHome, 'registries.json'), JSON.stringify([
|
|
69
69
|
{ name: 'sano-registry', remote: 'https://example.invalid/sano.git' },
|
|
@@ -79,8 +79,20 @@ function seedMixedRegistries() {
|
|
|
79
79
|
minCliVersion: '8.5.0',
|
|
80
80
|
orchestrator: { name: 'proceso-roto', appliesWhen: 'cuando algo pasa' },
|
|
81
81
|
}));
|
|
82
|
-
fs_1.default.writeFileSync(path_1.default.join(sanoRoot, 'skills', '
|
|
83
|
-
|
|
82
|
+
fs_1.default.writeFileSync(path_1.default.join(sanoRoot, 'skills', 'proceso-sano', 'SKILL.md'), '---\nname: proceso-sano\nversion: "1.0.0"\n---\n\n# proceso-sano\n');
|
|
83
|
+
return home;
|
|
84
|
+
}
|
|
85
|
+
function seedPhantomRegistry() {
|
|
86
|
+
const home = fs_1.default.realpathSync(fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'awm-ctx-e2e-phantom-')));
|
|
87
|
+
const awmHome = path_1.default.join(home, '.awm');
|
|
88
|
+
const root = path_1.default.join(awmHome, 'registries', 'test-registry');
|
|
89
|
+
fs_1.default.mkdirSync(path_1.default.join(root, 'skills', 'real-skill'), { recursive: true });
|
|
90
|
+
fs_1.default.writeFileSync(path_1.default.join(awmHome, 'registries.json'), JSON.stringify([{ name: 'test-registry', remote: 'https://example.invalid/test.git' }]));
|
|
91
|
+
fs_1.default.writeFileSync(path_1.default.join(root, 'awm-registry.json'), JSON.stringify({
|
|
92
|
+
minCliVersion: '8.5.0',
|
|
93
|
+
orchestrator: { name: 'phantom-process', appliesWhen: 'cuando hay una tarea sin plan', terminatesTo: 'development-process' },
|
|
94
|
+
}));
|
|
95
|
+
fs_1.default.writeFileSync(path_1.default.join(root, 'skills', 'real-skill', 'SKILL.md'), '---\nname: real-skill\nversion: "1.0.0"\n---\n\n# real-skill\n');
|
|
84
96
|
return home;
|
|
85
97
|
}
|
|
86
98
|
function run(home, ...args) {
|
|
@@ -142,3 +154,41 @@ describe('awm context orchestrators -- diagnosticos + sanos mezclados (binario r
|
|
|
142
154
|
expect(r.stderr).toContain('terminatesTo');
|
|
143
155
|
});
|
|
144
156
|
});
|
|
157
|
+
describe('awm context orchestrators -- declaracion phantom (binario real)', () => {
|
|
158
|
+
let home;
|
|
159
|
+
beforeAll(() => { home = seedPhantomRegistry(); });
|
|
160
|
+
afterAll(() => fs_1.default.rmSync(home, { recursive: true, force: true }));
|
|
161
|
+
it('--verify devuelve 2 y diagnostica una declaracion sin skill resoluble', () => {
|
|
162
|
+
const r = run(home, 'context', 'orchestrators', '--verify', 'phantom-process');
|
|
163
|
+
expect(r.status).toBe(2);
|
|
164
|
+
expect(r.stderr).toMatch(/phantom-process/);
|
|
165
|
+
expect(r.stderr).toMatch(/declaration dropped|skill/i);
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
describe('awm context orchestrators -- dropped sanitized collision (real binary)', () => {
|
|
169
|
+
let home;
|
|
170
|
+
beforeAll(() => {
|
|
171
|
+
home = seedPhantomRegistry();
|
|
172
|
+
const awmHome = path_1.default.join(home, '.awm');
|
|
173
|
+
const phantomRoot = path_1.default.join(awmHome, 'registries', 'test-registry');
|
|
174
|
+
const retainedRoot = path_1.default.join(awmHome, 'registries', 'retained');
|
|
175
|
+
fs_1.default.writeFileSync(path_1.default.join(awmHome, 'registries.json'), JSON.stringify([
|
|
176
|
+
{ name: 'test-registry', remote: 'unused' }, { name: 'retained', remote: 'unused' },
|
|
177
|
+
]));
|
|
178
|
+
fs_1.default.writeFileSync(path_1.default.join(phantomRoot, 'awm-registry.json'), JSON.stringify({
|
|
179
|
+
orchestrator: { name: 'foo_bar', appliesWhen: 'x', terminatesTo: 'none' },
|
|
180
|
+
}));
|
|
181
|
+
fs_1.default.mkdirSync(path_1.default.join(retainedRoot, 'skills', 'foobar'), { recursive: true });
|
|
182
|
+
fs_1.default.writeFileSync(path_1.default.join(retainedRoot, 'skills', 'foobar', 'SKILL.md'), '# foobar\n');
|
|
183
|
+
fs_1.default.writeFileSync(path_1.default.join(retainedRoot, 'awm-registry.json'), JSON.stringify({
|
|
184
|
+
orchestrator: { name: 'foobar', appliesWhen: 'x', terminatesTo: 'none' },
|
|
185
|
+
}));
|
|
186
|
+
});
|
|
187
|
+
afterAll(() => fs_1.default.rmSync(home, { recursive: true, force: true }));
|
|
188
|
+
it('verifies the retained raw name and rejects the dropped raw name', () => {
|
|
189
|
+
expect(run(home, 'context', 'orchestrators', '--verify', 'foobar').status).toBe(0);
|
|
190
|
+
const dropped = run(home, 'context', 'orchestrators', '--verify', 'foo_bar');
|
|
191
|
+
expect(dropped.status).toBe(2);
|
|
192
|
+
expect(dropped.stderr).toContain('declaration dropped');
|
|
193
|
+
});
|
|
194
|
+
});
|
|
@@ -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
|
+
});
|
|
@@ -42,6 +42,9 @@ function createFixture() {
|
|
|
42
42
|
fs_1.default.copyFileSync(path_1.default.join(project, 'eslint-tool-package.json'), path_1.default.join(project, 'node_modules', 'eslint', 'package.json'));
|
|
43
43
|
fs_1.default.mkdirSync(path_1.default.dirname(registryRoot), { recursive: true });
|
|
44
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'));
|
|
45
48
|
fs_1.default.writeFileSync(path_1.default.join(awmHome, 'registries.json'), JSON.stringify([{ name: 'baseline', remote: 'fixture' }]));
|
|
46
49
|
return { root, project, awmHome, registryRoot };
|
|
47
50
|
}
|
|
@@ -60,7 +63,8 @@ function runAwm(fixture, ...args) {
|
|
|
60
63
|
});
|
|
61
64
|
}
|
|
62
65
|
function json(result) {
|
|
63
|
-
|
|
66
|
+
if (result.status !== 0)
|
|
67
|
+
throw new Error(`CLI failed: ${result.stdout ?? ''}\n${result.stderr ?? ''}`);
|
|
64
68
|
if (!(result.stdout ?? '').trim())
|
|
65
69
|
throw new Error(`coverage emitted no JSON: stderr=${result.stderr ?? ''}`);
|
|
66
70
|
return JSON.parse(result.stdout ?? '');
|
|
@@ -116,6 +120,7 @@ test('compiled sensors run returns nonzero while preserving parseable not_certif
|
|
|
116
120
|
test('compiled status reports static READY without writing or executing the project sensor (R6, R6.2)', () => {
|
|
117
121
|
const fixture = createFixture();
|
|
118
122
|
try {
|
|
123
|
+
fs_1.default.rmSync(path_1.default.join(fixture.project, '.awm', 'sensors.json'));
|
|
119
124
|
const localBin = path_1.default.join(fixture.project, 'node_modules', '.bin', 'eslint');
|
|
120
125
|
fs_1.default.mkdirSync(path_1.default.dirname(localBin), { recursive: true });
|
|
121
126
|
fs_1.default.writeFileSync(localBin, 'this fixture must never be executed by status\n');
|
|
@@ -143,10 +148,13 @@ test('legacy coverage stays unverified, init migrates explicitly, and version dr
|
|
|
143
148
|
id: 'lint-errors', status: 'unverifiable', detectors: expect.arrayContaining([expect.objectContaining({ compatibility: expect.objectContaining({ state: 'compatible-unverified' }) })]),
|
|
144
149
|
})]));
|
|
145
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'));
|
|
146
154
|
const migrated = runCli(fixture, 'init', '--registry-root', fixture.registryRoot, '--pack', 'js-ts', '--no-configure');
|
|
147
155
|
expect(migrated.status).toBe(0);
|
|
148
156
|
const manifest = JSON.parse(fs_1.default.readFileSync(path_1.default.join(fixture.project, '.awm', 'sensors.json'), 'utf8'));
|
|
149
|
-
expect(manifest).toMatchObject({ schemaVersion:
|
|
157
|
+
expect(manifest).toMatchObject({ schemaVersion: 3, mode: 'project-sensors', pack: 'js-ts', source: { registry: 'baseline' }, sensors: { lint: { variantId: 'eslint-10' } } });
|
|
150
158
|
const certified = json(runCli(fixture, 'coverage', '--json'));
|
|
151
159
|
expect(certified.static.classes).toEqual(expect.arrayContaining([expect.objectContaining({
|
|
152
160
|
id: 'lint-errors', status: 'covered', detectors: expect.arrayContaining([expect.objectContaining({ compatibility: expect.objectContaining({ state: 'certified' }) })]),
|
|
@@ -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.1",
|
|
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
|