agentic-workflow-manager 9.2.1 → 9.4.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.
@@ -0,0 +1,293 @@
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 os_1 = __importDefault(require("os"));
8
+ const path_1 = __importDefault(require("path"));
9
+ describe('checkCurrentness', () => {
10
+ let root;
11
+ beforeEach(() => {
12
+ root = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'awm-currentness-'));
13
+ jest.resetModules();
14
+ });
15
+ afterEach(() => {
16
+ jest.useRealTimers();
17
+ fs_1.default.rmSync(root, { recursive: true, force: true });
18
+ });
19
+ function deps(overrides = {}) {
20
+ return {
21
+ cliVersion: () => '1.2.3',
22
+ now: () => 1_700_000_000_000,
23
+ fetch: jest.fn().mockResolvedValue({
24
+ ok: true,
25
+ text: async () => JSON.stringify({ 'dist-tags': { latest: '1.2.3' } }),
26
+ }),
27
+ readPreferences: () => ({ pins: {} }),
28
+ listRegistries: () => [{ name: 'baseline', remote: 'https://example.test/team/repo.git', contentRoot: '/registry' }],
29
+ git: jest.fn().mockImplementation(async (_cwd, args) => {
30
+ if (args[0] === 'ls-remote')
31
+ return `${'a'.repeat(40)}\trefs/tags/v1.2.3\n`;
32
+ if (args[0] === 'remote')
33
+ return 'https://example.test/team/repo.git\n';
34
+ if (args[0] === 'rev-parse')
35
+ return `${'a'.repeat(40)}\n`;
36
+ if (args[0] === 'describe')
37
+ return 'v1.2.3\n';
38
+ throw new Error(`unexpected git invocation: ${args.join(' ')}`);
39
+ }),
40
+ ...overrides,
41
+ };
42
+ }
43
+ it('reports authoritative current CLI and registry without exposing URL credentials', async () => {
44
+ const { checkCurrentness } = require('../../../src/core/currentness/check');
45
+ const result = await checkCurrentness(root, deps());
46
+ expect(result.components).toEqual([
47
+ expect.objectContaining({ component: 'cli', installed: '1.2.3', latest: '1.2.3', status: 'current', channel: 'stable' }),
48
+ expect.objectContaining({ component: 'registry:baseline', installed: 'v1.2.3', latest: 'v1.2.3', status: 'current', channel: 'stable' }),
49
+ ]);
50
+ expect(JSON.stringify(result)).not.toContain('secret');
51
+ expect(result.components[1].source).toBe('https://example.test/team/repo.git');
52
+ });
53
+ it('marks an older pinned registry as pinned-behind with the unpin remedy', async () => {
54
+ const { checkCurrentness } = require('../../../src/core/currentness/check');
55
+ const result = await checkCurrentness(root, deps({
56
+ readPreferences: () => ({ pins: { baseline: '1.0.0' } }),
57
+ git: jest.fn().mockImplementation(async (_cwd, args) => {
58
+ if (args[0] === 'ls-remote')
59
+ return `${'b'.repeat(40)}\trefs/tags/v1.1.0\n${'a'.repeat(40)}\trefs/tags/v1.0.0\n`;
60
+ if (args[0] === 'remote')
61
+ return 'https://example.test/team/repo.git\n';
62
+ if (args[0] === 'rev-parse')
63
+ return `${'a'.repeat(40)}\n`;
64
+ if (args[0] === 'describe')
65
+ return 'v1.0.0\n';
66
+ throw new Error('unexpected');
67
+ }),
68
+ }));
69
+ expect(result.components[1]).toEqual(expect.objectContaining({
70
+ status: 'pinned-behind',
71
+ pin: '1.0.0',
72
+ remedy: 'awm unpin baseline && awm update --yes',
73
+ }));
74
+ });
75
+ it('marks an npm latest version above the max-safe numeric boundary as stale', async () => {
76
+ const { checkCurrentness } = require('../../../src/core/currentness/check');
77
+ const result = await checkCurrentness(root, deps({
78
+ cliVersion: () => '1.9007199254740992.0',
79
+ fetch: jest.fn().mockResolvedValue({
80
+ ok: true,
81
+ text: async () => JSON.stringify({ 'dist-tags': { latest: '1.9007199254740993.0' } }),
82
+ }),
83
+ }));
84
+ expect(result.components[0]).toEqual(expect.objectContaining({
85
+ installed: '1.9007199254740992.0',
86
+ latest: '1.9007199254740993.0',
87
+ status: 'stale',
88
+ }));
89
+ });
90
+ it('marks a registry tag above the max-safe numeric boundary as stale', async () => {
91
+ const installed = 'v1.9007199254740992.0';
92
+ const latest = 'v1.9007199254740993.0';
93
+ const { checkCurrentness } = require('../../../src/core/currentness/check');
94
+ const result = await checkCurrentness(root, deps({
95
+ git: jest.fn().mockImplementation(async (_cwd, args) => {
96
+ if (args[0] === 'ls-remote')
97
+ return `${'b'.repeat(40)}\trefs/tags/${latest}\n${'a'.repeat(40)}\trefs/tags/${installed}\n`;
98
+ if (args[0] === 'remote')
99
+ return 'https://example.test/team/repo.git\n';
100
+ if (args[0] === 'rev-parse')
101
+ return `${'a'.repeat(40)}\n`;
102
+ if (args[0] === 'describe')
103
+ return `${installed}\n`;
104
+ throw new Error('unexpected');
105
+ }),
106
+ }));
107
+ expect(result.components[1]).toEqual(expect.objectContaining({ installed, latest, status: 'stale' }));
108
+ });
109
+ it('discovers an unprefixed stable SemVer tag while ignoring prereleases', async () => {
110
+ const { checkCurrentness } = require('../../../src/core/currentness/check');
111
+ const result = await checkCurrentness(root, deps({
112
+ git: jest.fn().mockImplementation(async (_cwd, args) => {
113
+ if (args[0] === 'ls-remote')
114
+ return `${'a'.repeat(40)}\trefs/tags/1.2.4\n${'b'.repeat(40)}\trefs/tags/1.2.5-rc.1\n`;
115
+ if (args[0] === 'remote')
116
+ return 'https://example.test/team/repo.git\n';
117
+ if (args[0] === 'rev-parse')
118
+ return `${'a'.repeat(40)}\n`;
119
+ if (args[0] === 'describe')
120
+ return '1.2.4\n';
121
+ throw new Error('unexpected');
122
+ }),
123
+ }));
124
+ expect(result.components[1]).toEqual(expect.objectContaining({
125
+ installed: '1.2.4', latest: '1.2.4', status: 'current',
126
+ }));
127
+ });
128
+ it('marks a lower installed version stale even when it shares the latest tag SHA', async () => {
129
+ const sharedSha = 'a'.repeat(40);
130
+ const { checkCurrentness } = require('../../../src/core/currentness/check');
131
+ const result = await checkCurrentness(root, deps({
132
+ git: jest.fn().mockImplementation(async (_cwd, args) => {
133
+ if (args[0] === 'ls-remote')
134
+ return `${sharedSha}\trefs/tags/v1.2.4\n`;
135
+ if (args[0] === 'remote')
136
+ return 'https://example.test/team/repo.git\n';
137
+ if (args[0] === 'rev-parse')
138
+ return `${sharedSha}\n`;
139
+ if (args[0] === 'describe')
140
+ return 'v1.2.3\n';
141
+ throw new Error('unexpected');
142
+ }),
143
+ }));
144
+ expect(result.components[1]).toEqual(expect.objectContaining({
145
+ installed: 'v1.2.3', latest: 'v1.2.4', status: 'stale', remedy: 'awm update --yes',
146
+ }));
147
+ });
148
+ it.each([
149
+ ['branch provenance', async (_cwd, args) => args[0] === 'describe' ? Promise.reject(new Error('not a tag')) : args[0] === 'ls-remote' ? `${'a'.repeat(40)}\trefs/tags/v1.2.3\n` : args[0] === 'remote' ? 'https://example.test/team/repo.git\n' : `${'a'.repeat(40)}\n`],
150
+ ['malformed npm semver', undefined],
151
+ ])('fails closed for %s', async (name, git) => {
152
+ const { checkCurrentness } = require('../../../src/core/currentness/check');
153
+ const result = await checkCurrentness(root, deps(name === 'malformed npm semver'
154
+ ? { fetch: jest.fn().mockResolvedValue({ ok: true, text: async () => JSON.stringify({ 'dist-tags': { latest: 'latest' } }) }) }
155
+ : { git }));
156
+ expect(result.components.some((c) => c.status === 'unverifiable')).toBe(true);
157
+ });
158
+ it('bypasses passive update controls and makes no writes', async () => {
159
+ const { checkCurrentness } = require('../../../src/core/currentness/check');
160
+ const before = fs_1.default.readdirSync(root);
161
+ const fetch = jest.fn().mockResolvedValue({ ok: false, text: async () => '' });
162
+ const result = await checkCurrentness(root, deps({ fetch, env: { AWM_NO_UPDATE_CHECK: '1' } }));
163
+ expect(fetch).toHaveBeenCalledTimes(1);
164
+ expect(result.components[0]).toEqual(expect.objectContaining({ status: 'unverifiable' }));
165
+ expect(fs_1.default.readdirSync(root)).toEqual(before);
166
+ });
167
+ it('returns a complete strict report when the local registry inventory is malformed', async () => {
168
+ const { checkCurrentness } = require('../../../src/core/currentness/check');
169
+ const result = await checkCurrentness(root, deps({
170
+ listRegistries: () => { throw new Error('Invalid registries config at /private/registries.json'); },
171
+ }));
172
+ expect(result.components).toEqual(expect.arrayContaining([
173
+ expect.objectContaining({ component: 'cli', status: 'current' }),
174
+ expect.objectContaining({
175
+ component: 'registry:inventory', status: 'unverifiable',
176
+ source: '[configured registry inventory]',
177
+ remedy: 'Repair the local registry inventory and rerun strict preflight.',
178
+ }),
179
+ ]));
180
+ expect(JSON.stringify(result)).not.toContain('/private/registries.json');
181
+ });
182
+ it('settles deterministically when an injected fetch ignores abort and later rejects', async () => {
183
+ jest.useFakeTimers();
184
+ let rejectFetch;
185
+ const fetch = jest.fn(() => new Promise((_resolve, reject) => { rejectFetch = reject; }));
186
+ const { checkCurrentness } = require('../../../src/core/currentness/check');
187
+ const pending = checkCurrentness(root, deps({ fetch }));
188
+ await jest.advanceTimersByTimeAsync(2_000);
189
+ await expect(pending).resolves.toEqual(expect.objectContaining({
190
+ components: expect.arrayContaining([expect.objectContaining({ component: 'cli', status: 'unverifiable' })]),
191
+ }));
192
+ rejectFetch(new Error('late fetch failure'));
193
+ await Promise.resolve();
194
+ });
195
+ it('settles deterministically when an injected Git transport ignores its timeout and later rejects', async () => {
196
+ jest.useFakeTimers();
197
+ let rejectGit;
198
+ const git = jest.fn().mockImplementation(async (_cwd, args) => {
199
+ if (args[0] === 'ls-remote')
200
+ return new Promise((_resolve, reject) => { rejectGit = reject; });
201
+ if (args[0] === 'remote')
202
+ return 'https://example.test/team/repo.git\n';
203
+ if (args[0] === 'rev-parse')
204
+ return `${'a'.repeat(40)}\n`;
205
+ if (args[0] === 'describe')
206
+ return 'v1.2.3\n';
207
+ throw new Error('unexpected');
208
+ });
209
+ const { checkCurrentness } = require('../../../src/core/currentness/check');
210
+ const pending = checkCurrentness(root, deps({ git }));
211
+ await jest.advanceTimersByTimeAsync(2_000);
212
+ await expect(pending).resolves.toEqual(expect.objectContaining({
213
+ components: expect.arrayContaining([expect.objectContaining({ component: 'registry:baseline', status: 'unverifiable' })]),
214
+ }));
215
+ rejectGit(new Error('late Git failure'));
216
+ await Promise.resolve();
217
+ });
218
+ it('rejects oversized npm metadata before attempting JSON parsing', async () => {
219
+ const parse = jest.fn(() => ({ 'dist-tags': { latest: '1.2.3' } }));
220
+ const { checkCurrentness } = require('../../../src/core/currentness/check');
221
+ const result = await checkCurrentness(root, deps({
222
+ fetch: jest.fn().mockResolvedValue({
223
+ ok: true,
224
+ text: async () => 'x'.repeat(64 * 1024 + 1),
225
+ json: parse,
226
+ }),
227
+ }));
228
+ expect(result.components[0]).toEqual(expect.objectContaining({ status: 'unverifiable' }));
229
+ expect(parse).not.toHaveBeenCalled();
230
+ });
231
+ it.each([
232
+ 'http://example.test/team/repo.git',
233
+ 'ftp://example.test/team/repo.git',
234
+ 'https://username:password@example.test/team/repo.git',
235
+ 'ssh://git:secret@example.test/team/repo.git',
236
+ 'git@example.test:team/repo.git?token=secret',
237
+ ])('rejects non-authoritative transport %s before Git invocation', async (remote) => {
238
+ const git = jest.fn();
239
+ const { checkCurrentness } = require('../../../src/core/currentness/check');
240
+ const result = await checkCurrentness(root, deps({
241
+ listRegistries: () => [{ name: 'baseline', remote, contentRoot: '/registry' }],
242
+ git,
243
+ }));
244
+ expect(result.components[1]).toEqual(expect.objectContaining({
245
+ source: '[configured remote]',
246
+ status: 'unverifiable',
247
+ }));
248
+ expect(git).not.toHaveBeenCalled();
249
+ });
250
+ it.each([
251
+ ['ssh://username@example.test/team/repo.git', 'ssh://example.test/team/repo.git'],
252
+ ['username@github.example:team/repo.git', 'github.example:team/repo.git'],
253
+ ])('accepts auth-free SSH and SCP remotes while rendering a safe source for %s', async (remote, expectedSource) => {
254
+ const { checkCurrentness } = require('../../../src/core/currentness/check');
255
+ const result = await checkCurrentness(root, deps({
256
+ listRegistries: () => [{ name: 'baseline', remote, contentRoot: '/registry' }],
257
+ git: jest.fn().mockImplementation(async (_cwd, args) => {
258
+ if (args[0] === 'ls-remote')
259
+ return `${'a'.repeat(40)}\trefs/tags/v1.2.3\n`;
260
+ if (args[0] === 'remote')
261
+ return `${remote}\n`;
262
+ if (args[0] === 'rev-parse')
263
+ return `${'a'.repeat(40)}\n`;
264
+ if (args[0] === 'describe')
265
+ return 'v1.2.3\n';
266
+ throw new Error('unexpected');
267
+ }),
268
+ }));
269
+ expect(result.components[1].source).toBe(expectedSource);
270
+ expect(JSON.stringify(result)).not.toContain('username');
271
+ expect(JSON.stringify(result)).not.toContain('password');
272
+ expect(JSON.stringify(result)).not.toContain('secret');
273
+ });
274
+ it('treats the peeled commit of an annotated stable tag as authoritative', async () => {
275
+ const tagObject = 'b'.repeat(40);
276
+ const peeledCommit = 'a'.repeat(40);
277
+ const { checkCurrentness } = require('../../../src/core/currentness/check');
278
+ const result = await checkCurrentness(root, deps({
279
+ git: jest.fn().mockImplementation(async (_cwd, args) => {
280
+ if (args[0] === 'ls-remote')
281
+ return `${tagObject}\trefs/tags/v1.2.3\n${peeledCommit}\trefs/tags/v1.2.3^{}\n`;
282
+ if (args[0] === 'remote')
283
+ return 'https://example.test/team/repo.git\n';
284
+ if (args[0] === 'rev-parse')
285
+ return `${peeledCommit}\n`;
286
+ if (args[0] === 'describe')
287
+ return 'v1.2.3\n';
288
+ throw new Error('unexpected');
289
+ }),
290
+ }));
291
+ expect(result.components[1]).toEqual(expect.objectContaining({ status: 'current', latest: 'v1.2.3' }));
292
+ });
293
+ });