agentic-workflow-manager 9.8.0 → 9.10.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/job/gate.js +18 -0
- package/dist/src/commands/job/index.js +61 -0
- package/dist/src/commands/model-policy/index.js +94 -0
- package/dist/src/commands/plan/index.js +59 -2
- package/dist/src/commands/watch/apply.js +32 -0
- package/dist/src/core/admission/index.js +52 -2
- package/dist/src/core/admission/registry-contracts.js +1 -1
- package/dist/src/core/journal/requests.js +1 -1
- package/dist/src/core/journal/types.js +22 -1
- package/dist/src/core/model-policy/canonical.js +18 -0
- package/dist/src/core/model-policy/capabilities.js +197 -0
- package/dist/src/core/model-policy/capability-types.js +2 -0
- package/dist/src/core/model-policy/digest.js +35 -0
- package/dist/src/core/model-policy/journal.js +124 -0
- package/dist/src/core/model-policy/paths.js +12 -0
- package/dist/src/core/model-policy/resolve.js +155 -0
- package/dist/src/core/model-policy/store.js +189 -0
- package/dist/src/core/model-policy/types.js +2 -0
- package/dist/src/core/model-policy/validate.js +112 -0
- package/dist/src/core/plan/identity.js +4 -2
- package/dist/src/core/plan/validate.js +15 -16
- package/dist/src/index.js +2 -0
- package/dist/tests/commands/job/gate-reconcile.test.js +16 -0
- package/dist/tests/commands/job/gate.test.js +9 -0
- package/dist/tests/commands/job/routing-report-cli.test.js +46 -0
- package/dist/tests/commands/job/routing.test.js +11 -0
- package/dist/tests/commands/model-policy/index.test.js +117 -0
- package/dist/tests/commands/plan/index.test.js +52 -0
- package/dist/tests/commands/watch/apply.test.js +12 -0
- package/dist/tests/core/admission/index.test.js +53 -0
- package/dist/tests/core/model-policy/capabilities.test.js +365 -0
- package/dist/tests/core/model-policy/digest.test.js +117 -0
- package/dist/tests/core/model-policy/journal.test.js +46 -0
- package/dist/tests/core/model-policy/policy.test.js +47 -0
- package/dist/tests/core/model-policy/resolve.test.js +185 -0
- package/dist/tests/core/model-policy/store.test.js +177 -0
- package/dist/tests/core/plan/validate.test.js +40 -2
- package/dist/tests/core/secure-fs/native-bridge.test.js +81 -4
- package/dist/tests/structural/directory-fsync-is-single-source.test.js +97 -0
- package/dist/tests/structural/native-release-artifacts.test.js +14 -0
- package/package.json +3 -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
|
@@ -0,0 +1,97 @@
|
|
|
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
|
+
// Structural guard, same family as symlink-type-is-explicit.test.ts.
|
|
7
|
+
//
|
|
8
|
+
// fsync-ing a DIRECTORY file descriptor is a POSIX-only capability. On Windows
|
|
9
|
+
// `fs.openSync(dir, 'r')` succeeds and the following `fs.fsyncSync(fd)` always
|
|
10
|
+
// fails with EPERM — libuv's mapping of `FlushFileBuffers` on a directory
|
|
11
|
+
// handle, which Win32 rejects categorically. That is not a durability failure
|
|
12
|
+
// (NTFS journals the rename itself); the mechanism simply does not exist there.
|
|
13
|
+
//
|
|
14
|
+
// `atomic-file.fsyncDirSync` is where that decision lives, and it is deliberately
|
|
15
|
+
// narrow: degrade ONLY on win32 + EPERM at the fsync, keep throwing everywhere
|
|
16
|
+
// else. Re-implementing the open/fsync/close sequence inline re-creates the gap
|
|
17
|
+
// without the exception, and it fails on Windows and nowhere else — invisible to
|
|
18
|
+
// every local run, and behind CI's `--bail` it costs one full matrix round to
|
|
19
|
+
// surface. That is exactly how `tests/core/model-policy/store.test.ts` broke the
|
|
20
|
+
// Windows legs of the matrix while all four POSIX legs stayed green.
|
|
21
|
+
//
|
|
22
|
+
// So the rule is about the CALL, not about any one call site: a directory fd may
|
|
23
|
+
// be flushed only through `fsyncDirSync`. This scans `tests` as well as `src` —
|
|
24
|
+
// the regression that motivated it lived in a test double, and a guard that only
|
|
25
|
+
// watched `src` would have reported a clean bill of health.
|
|
26
|
+
const fs_1 = __importDefault(require("fs"));
|
|
27
|
+
const path_1 = __importDefault(require("path"));
|
|
28
|
+
const CLI = path_1.default.join(__dirname, '..', '..');
|
|
29
|
+
const ROOTS = ['src', 'tests'];
|
|
30
|
+
/** The single source of the policy: the one file allowed to flush a directory fd. */
|
|
31
|
+
const SINGLE_SOURCE = path_1.default.join('src', 'core', 'atomic-file.ts');
|
|
32
|
+
/** This guard itself, which must quote the forbidden shape to describe and test it. */
|
|
33
|
+
const THIS_GUARD = path_1.default.join('tests', 'structural', 'directory-fsync-is-single-source.test.ts');
|
|
34
|
+
function sourceFiles(dir) {
|
|
35
|
+
const out = [];
|
|
36
|
+
for (const entry of fs_1.default.readdirSync(dir, { withFileTypes: true })) {
|
|
37
|
+
const full = path_1.default.join(dir, entry.name);
|
|
38
|
+
if (entry.isDirectory())
|
|
39
|
+
out.push(...sourceFiles(full));
|
|
40
|
+
else if (entry.name.endsWith('.ts'))
|
|
41
|
+
out.push(full);
|
|
42
|
+
}
|
|
43
|
+
return out;
|
|
44
|
+
}
|
|
45
|
+
/** True when `lines[i]` opens something that reads as a directory. Deliberately
|
|
46
|
+
* shallow — it recognises the shapes this codebase actually writes. Something
|
|
47
|
+
* cleverer that defeats it should be calling `fsyncDirSync` instead. */
|
|
48
|
+
function opensADirectory(line) {
|
|
49
|
+
const open = /\bfs\.openSync\s*\(\s*([^,]+),/.exec(line);
|
|
50
|
+
if (!open)
|
|
51
|
+
return false;
|
|
52
|
+
const target = open[1];
|
|
53
|
+
return /\bpath\.dirname\s*\(|\bdirname\s*\(/.test(target) || /\b(dir|Dir|root|Root)\b|Dir\b/.test(target);
|
|
54
|
+
}
|
|
55
|
+
/** True when the fd opened at `lines[i]` is flushed within the next few lines. */
|
|
56
|
+
function flushedNearby(lines, i) {
|
|
57
|
+
return /\bfsyncSync\s*\(/.test(lines.slice(i, i + 4).join('\n'));
|
|
58
|
+
}
|
|
59
|
+
describe('directory fsync has exactly one implementation', () => {
|
|
60
|
+
it('no file outside atomic-file.ts opens a directory and flushes it inline', () => {
|
|
61
|
+
const offenders = [];
|
|
62
|
+
for (const root of ROOTS) {
|
|
63
|
+
for (const file of sourceFiles(path_1.default.join(CLI, root))) {
|
|
64
|
+
const relative = path_1.default.relative(CLI, file);
|
|
65
|
+
if (relative === SINGLE_SOURCE || relative === THIS_GUARD)
|
|
66
|
+
continue;
|
|
67
|
+
const lines = fs_1.default.readFileSync(file, 'utf-8').split('\n');
|
|
68
|
+
lines.forEach((line, i) => {
|
|
69
|
+
if (!opensADirectory(line) || !flushedNearby(lines, i))
|
|
70
|
+
return;
|
|
71
|
+
offenders.push(`${relative}:${i + 1}: ${line.trim()}`);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
expect(offenders).toEqual([]);
|
|
76
|
+
});
|
|
77
|
+
it('the guard actually rejects the shape that broke the Windows matrix', () => {
|
|
78
|
+
// Without this, a guard whose regex silently stopped matching would read as
|
|
79
|
+
// a clean bill of health forever — the failure mode of every structural test.
|
|
80
|
+
const rejects = (lines, i) => opensADirectory(lines[i]) && flushedNearby(lines, i);
|
|
81
|
+
// Verbatim shape of the store.test.ts regression this guard exists to stop.
|
|
82
|
+
const regression = [
|
|
83
|
+
'fs.renameSync(temporary, file);',
|
|
84
|
+
"const parent = fs.openSync(path.dirname(file), 'r');",
|
|
85
|
+
'try { fs.fsyncSync(parent); } finally { fs.closeSync(parent); }',
|
|
86
|
+
];
|
|
87
|
+
expect(rejects(regression, 1)).toBe(true);
|
|
88
|
+
// The sanctioned replacement carries no directory open at all.
|
|
89
|
+
expect(regression.map((_, i) => i).some(i => rejects(['fsyncDirSync(path.dirname(file));'], 0))).toBe(false);
|
|
90
|
+
// Flushing a FILE descriptor stays legal — that is a real, portable fsync.
|
|
91
|
+
const fileFlush = ["const fd = fs.openSync(temporary, 'wx', 0o600);", 'fs.fsyncSync(fd);'];
|
|
92
|
+
expect(rejects(fileFlush, 0)).toBe(false);
|
|
93
|
+
// Opening a directory without flushing it (readdir-style handles) stays legal.
|
|
94
|
+
const openOnly = ["const parent = fs.openSync(path.dirname(file), 'r');", 'fs.closeSync(parent);'];
|
|
95
|
+
expect(rejects(openOnly, 0)).toBe(false);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
@@ -35,4 +35,18 @@ describe('native release artifacts', () => {
|
|
|
35
35
|
expect(gitignore).toContain('cli/native-artifacts/');
|
|
36
36
|
expect(tracked).toBe('');
|
|
37
37
|
});
|
|
38
|
+
it('builds the fault-injection addon before every Jest suite without publishing it', () => {
|
|
39
|
+
const pkg = JSON.parse(fs_1.default.readFileSync(path_1.default.join(CLI_ROOT, 'package.json'), 'utf8'));
|
|
40
|
+
expect(pkg.scripts['native:test-build']).toContain('secure_fs_test');
|
|
41
|
+
for (const name of ['ci.yml', 'release.yml']) {
|
|
42
|
+
const workflow = fs_1.default.readFileSync(path_1.default.join(REPO_ROOT, '.github', 'workflows', name), 'utf8');
|
|
43
|
+
const testBuild = workflow.indexOf('run: npm run native:test-build');
|
|
44
|
+
const jest = workflow.indexOf('run: npx jest --runInBand --bail');
|
|
45
|
+
expect(testBuild).toBeGreaterThanOrEqual(0);
|
|
46
|
+
expect(testBuild).toBeLessThan(jest);
|
|
47
|
+
expect(workflow).toContain("'native/build/Release', 'secure_fs_test.node'");
|
|
48
|
+
expect(workflow).not.toContain('path: cli/prebuilds/${{ matrix.target }}/secure_fs_test.node');
|
|
49
|
+
expect(workflow).not.toContain('secure_fs_test.node\n');
|
|
50
|
+
}
|
|
51
|
+
});
|
|
38
52
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentic-workflow-manager",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.10.0",
|
|
4
4
|
"main": "dist/src/index.js",
|
|
5
5
|
"bin": {
|
|
6
6
|
"awm": "./dist/src/index.js"
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
"doc": "docs"
|
|
19
19
|
},
|
|
20
20
|
"scripts": {
|
|
21
|
-
"native:build": "node-gyp
|
|
21
|
+
"native:build": "node-gyp configure --directory native && node-gyp build --directory native -- secure_fs && node scripts/package-native.js",
|
|
22
|
+
"native:test-build": "node-gyp configure --directory native && node-gyp build --directory native -- secure_fs_test",
|
|
22
23
|
"native:package-check": "node scripts/assert-native-package.js",
|
|
23
24
|
"build": "tsc && chmod +x dist/src/index.js",
|
|
24
25
|
"typecheck": "tsc --noEmit",
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|