agentic-workflow-manager 9.2.0 → 9.2.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/update.js +24 -2
- package/dist/src/core/registries.js +18 -2
- package/dist/src/core/versioning.js +2 -2
- package/dist/tests/commands/update.test.js +31 -0
- package/dist/tests/core/registries-sync.test.js +29 -11
- package/dist/tests/core/versioning.test.js +5 -5
- package/package.json +1 -1
|
@@ -21,6 +21,7 @@ exports.runUpdateCore = runUpdateCore;
|
|
|
21
21
|
// instead (which already imports clack at the top and is never `require()`d
|
|
22
22
|
// by tests).
|
|
23
23
|
const picocolors_1 = __importDefault(require("picocolors"));
|
|
24
|
+
const versioning_1 = require("../core/versioning");
|
|
24
25
|
const paths_1 = require("../core/paths");
|
|
25
26
|
const config_1 = require("../utils/config");
|
|
26
27
|
const agent_targets_1 = require("../core/agent-targets");
|
|
@@ -89,8 +90,29 @@ async function runUpdateCore(options = {}, deps = {}) {
|
|
|
89
90
|
for (const r of registryResults) {
|
|
90
91
|
if (r.action === 'error')
|
|
91
92
|
console.warn(picocolors_1.default.yellow(` ⚠ registry ${r.name}: ${r.error}`));
|
|
92
|
-
else
|
|
93
|
-
|
|
93
|
+
else if (r.disposition === 'regressed') {
|
|
94
|
+
const latest = r.availableVersion && r.availableVersion !== r.version
|
|
95
|
+
? ` Latest available: ${r.availableVersion}.`
|
|
96
|
+
: '';
|
|
97
|
+
console.warn(picocolors_1.default.yellow(` ⚠ Registry ${r.name} regressed ${r.previousVersion ?? '(previous version)'} → ${r.version} `
|
|
98
|
+
+ `because it is pinned to v${(0, versioning_1.normalizePin)(r.pin ?? '')}.${latest} Run \`awm unpin ${r.name}\` to move forward.`));
|
|
99
|
+
}
|
|
100
|
+
else if (r.pin && r.availableVersion !== r.version) {
|
|
101
|
+
console.log(picocolors_1.default.yellow(` ✓ Registry ${r.name} resolved @ ${r.version} (pinned to v${(0, versioning_1.normalizePin)(r.pin)}; `
|
|
102
|
+
+ `latest available: ${r.availableVersion ?? 'unknown'}). Run \`awm unpin ${r.name}\` to move forward.`));
|
|
103
|
+
}
|
|
104
|
+
else if (r.disposition === 'unchanged') {
|
|
105
|
+
console.log(picocolors_1.default.dim(` ✓ Registry ${r.name} already at ${r.version}`));
|
|
106
|
+
}
|
|
107
|
+
else if (r.disposition === 'advanced') {
|
|
108
|
+
console.log(picocolors_1.default.green(` ✓ Registry ${r.name} updated @ ${r.version}`));
|
|
109
|
+
}
|
|
110
|
+
else if (r.disposition === 'installed') {
|
|
111
|
+
console.log(picocolors_1.default.green(` ✓ Registry ${r.name} installed @ ${r.version}`));
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
console.log(picocolors_1.default.green(` ✓ Registry ${r.name} resolved @ ${r.version}`));
|
|
115
|
+
}
|
|
94
116
|
}
|
|
95
117
|
const registries = {
|
|
96
118
|
configured: registryResults.length,
|
|
@@ -251,9 +251,11 @@ async function syncRegistries() {
|
|
|
251
251
|
await (0, simple_git_1.default)(reg.contentRoot).reset(['--hard']);
|
|
252
252
|
}
|
|
253
253
|
const git = (0, simple_git_1.default)(reg.contentRoot);
|
|
254
|
+
const previousVersion = freshClone ? null : await (0, versioning_1.currentVersion)(reg.contentRoot);
|
|
255
|
+
const versionOpts = (0, versioning_1.machineVersionOpts)(reg.name);
|
|
254
256
|
let resolved;
|
|
255
257
|
try {
|
|
256
|
-
resolved = await (0, versioning_1.resolveTargetRef)(reg.contentRoot,
|
|
258
|
+
resolved = await (0, versioning_1.resolveTargetRef)(reg.contentRoot, versionOpts);
|
|
257
259
|
await git.checkout(resolved.ref);
|
|
258
260
|
if (resolved.kind !== 'tag')
|
|
259
261
|
await git.pull('origin', resolved.ref);
|
|
@@ -266,10 +268,24 @@ async function syncRegistries() {
|
|
|
266
268
|
fs_1.default.rmSync(reg.contentRoot, { recursive: true, force: true });
|
|
267
269
|
throw e;
|
|
268
270
|
}
|
|
271
|
+
const version = resolved.kind === 'tag' ? `v${resolved.version}` : 'HEAD';
|
|
272
|
+
let disposition;
|
|
273
|
+
if (freshClone)
|
|
274
|
+
disposition = 'installed';
|
|
275
|
+
else if (resolved.kind !== 'tag' || previousVersion === null)
|
|
276
|
+
disposition = 'resolved';
|
|
277
|
+
else {
|
|
278
|
+
const direction = (0, versioning_1.compareSemver)(resolved.version, previousVersion);
|
|
279
|
+
disposition = direction > 0 ? 'advanced' : direction < 0 ? 'regressed' : 'unchanged';
|
|
280
|
+
}
|
|
269
281
|
results.push({
|
|
270
282
|
name: reg.name,
|
|
271
283
|
action: freshClone ? 'recloned' : 'pulled',
|
|
272
|
-
version
|
|
284
|
+
version,
|
|
285
|
+
disposition,
|
|
286
|
+
...(versionOpts.pin ? { pin: (0, versioning_1.normalizePin)(versionOpts.pin) } : {}),
|
|
287
|
+
...(previousVersion ? { previousVersion: `v${previousVersion}` } : {}),
|
|
288
|
+
...(resolved.kind === 'tag' ? { availableVersion: `v${resolved.latestVersion}` } : {}),
|
|
273
289
|
});
|
|
274
290
|
}
|
|
275
291
|
catch (e) {
|
|
@@ -69,7 +69,7 @@ async function resolveTargetRef(repoDir, opts) {
|
|
|
69
69
|
: 'the registry has no version tags';
|
|
70
70
|
throw new Error(`Pinned version ${want} not found in ${repoDir} — ${available}`);
|
|
71
71
|
}
|
|
72
|
-
return { kind: 'tag', ref: want, version: normalizePin(opts.pin) };
|
|
72
|
+
return { kind: 'tag', ref: want, version: normalizePin(opts.pin), latestVersion: tags[tags.length - 1].slice(1) };
|
|
73
73
|
}
|
|
74
74
|
const branch = await defaultBranch(git);
|
|
75
75
|
if (opts.channel === 'dev')
|
|
@@ -77,7 +77,7 @@ async function resolveTargetRef(repoDir, opts) {
|
|
|
77
77
|
if (tags.length === 0)
|
|
78
78
|
return { kind: 'head-fallback', ref: branch };
|
|
79
79
|
const latest = tags[tags.length - 1];
|
|
80
|
-
return { kind: 'tag', ref: latest, version: latest.slice(1) };
|
|
80
|
+
return { kind: 'tag', ref: latest, version: latest.slice(1), latestVersion: latest.slice(1) };
|
|
81
81
|
}
|
|
82
82
|
/** Versión checkouteada actual ("X.Y.Z") si HEAD coincide exactamente con un tag semver; null si sigue un branch. */
|
|
83
83
|
async function currentVersion(repoDir) {
|
|
@@ -125,6 +125,37 @@ describe('awm update — honest outcome', () => {
|
|
|
125
125
|
expect(result.registries).toEqual({ configured: 2, synced: 2, failed: [] });
|
|
126
126
|
expect(updateOutro(result)).toBe('✅ 2 registries, skills and hooks updated.');
|
|
127
127
|
});
|
|
128
|
+
it('una regresión causada por pin se advierte y explica cómo volver a avanzar', async () => {
|
|
129
|
+
const { runUpdateCore } = require('../../src/commands/update');
|
|
130
|
+
await runUpdateCore({}, deps({
|
|
131
|
+
syncRegistries: async () => [{
|
|
132
|
+
name: 'baseline',
|
|
133
|
+
action: 'pulled',
|
|
134
|
+
version: 'v1.0.0',
|
|
135
|
+
disposition: 'regressed',
|
|
136
|
+
pin: '1.0.0',
|
|
137
|
+
availableVersion: 'v1.1.0',
|
|
138
|
+
}],
|
|
139
|
+
}));
|
|
140
|
+
const output = [...logSpy.mock.calls, ...warnSpy.mock.calls].flat().join('\n');
|
|
141
|
+
expect(output).toMatch(/regress/i);
|
|
142
|
+
expect(output).toMatch(/v1\.0\.0/);
|
|
143
|
+
expect(output).toMatch(/v1\.1\.0/);
|
|
144
|
+
expect(output).toMatch(/awm unpin baseline/);
|
|
145
|
+
expect(output).not.toMatch(/Registry baseline updated/i);
|
|
146
|
+
});
|
|
147
|
+
it('un pin que ya coincide con el último release no recomienda unpin', async () => {
|
|
148
|
+
const { runUpdateCore } = require('../../src/commands/update');
|
|
149
|
+
await runUpdateCore({}, deps({
|
|
150
|
+
syncRegistries: async () => [{
|
|
151
|
+
name: 'baseline', action: 'pulled', version: 'v1.1.0', disposition: 'unchanged',
|
|
152
|
+
pin: 'v1.1.0', availableVersion: 'v1.1.0',
|
|
153
|
+
}],
|
|
154
|
+
}));
|
|
155
|
+
const output = [...logSpy.mock.calls, ...warnSpy.mock.calls].flat().join('\n');
|
|
156
|
+
expect(output).toMatch(/already at v1\.1\.0/i);
|
|
157
|
+
expect(output).not.toMatch(/awm unpin baseline/);
|
|
158
|
+
});
|
|
128
159
|
it('un registry que falló y NO dejó contenido en disco falla cerrado', async () => {
|
|
129
160
|
const { runUpdateCore } = require('../../src/commands/update');
|
|
130
161
|
const result = await runUpdateCore({}, deps({
|
|
@@ -79,7 +79,7 @@ describe('syncRegistries (git fixtures locales)', () => {
|
|
|
79
79
|
const source = makeSourceRepo(tmpWork, 'alpha');
|
|
80
80
|
m.writeRegistriesConfig([{ name: 'personal', remote: source }]);
|
|
81
81
|
const results = await m.syncRegistries();
|
|
82
|
-
expect(results).toEqual([{ name: 'personal', action: 'recloned', version: 'HEAD' }]);
|
|
82
|
+
expect(results).toEqual([{ name: 'personal', action: 'recloned', version: 'HEAD', disposition: 'installed' }]);
|
|
83
83
|
expect(fs_1.default.existsSync(path_1.default.join(tmpHome, '.awm/registries/personal/skills/alpha/SKILL.md'))).toBe(true);
|
|
84
84
|
});
|
|
85
85
|
it('pulls an existing clone and reports non-fatal errors per registry', async () => {
|
|
@@ -95,7 +95,7 @@ describe('syncRegistries (git fixtures locales)', () => {
|
|
|
95
95
|
GIT(source, 'add -A');
|
|
96
96
|
GIT(source, 'commit -qm v2');
|
|
97
97
|
const results = await m.syncRegistries();
|
|
98
|
-
expect(results[0]).toEqual({ name: 'personal', action: 'pulled', version: 'HEAD' });
|
|
98
|
+
expect(results[0]).toEqual({ name: 'personal', action: 'pulled', version: 'HEAD', disposition: 'resolved' });
|
|
99
99
|
expect(results[1].name).toBe('broken');
|
|
100
100
|
expect(results[1].action).toBe('error');
|
|
101
101
|
const synced = fs_1.default.readFileSync(path_1.default.join(tmpHome, '.awm/registries/personal/skills/alpha/SKILL.md'), 'utf-8');
|
|
@@ -127,7 +127,7 @@ describe('syncRegistries (git fixtures locales)', () => {
|
|
|
127
127
|
GIT(source, 'commit -qm unreleased');
|
|
128
128
|
m.writeRegistriesConfig([{ name: 'personal', remote: source }]);
|
|
129
129
|
const results = await m.syncRegistries();
|
|
130
|
-
expect(results).toEqual([{ name: 'personal', action: 'recloned', version: 'v1.0.0' }]);
|
|
130
|
+
expect(results).toEqual([{ name: 'personal', action: 'recloned', version: 'v1.0.0', disposition: 'installed', availableVersion: 'v1.0.0' }]);
|
|
131
131
|
const synced = fs_1.default.readFileSync(path_1.default.join(tmpHome, '.awm/registries/personal/skills/alpha/SKILL.md'), 'utf-8');
|
|
132
132
|
expect(synced).toContain('test skill'); // contenido del tag, no del HEAD
|
|
133
133
|
});
|
|
@@ -157,7 +157,9 @@ describe('syncRegistries (git fixtures locales)', () => {
|
|
|
157
157
|
fs_1.default.mkdirSync(awmDir, { recursive: true });
|
|
158
158
|
fs_1.default.writeFileSync(path_1.default.join(awmDir, 'preferences.json'), JSON.stringify({ defaultAgent: 'claude-code', installMethod: 'symlink', defaultScope: 'local', pins: { personal: '1.0.0' } }));
|
|
159
159
|
const results = await m.syncRegistries();
|
|
160
|
-
expect(results).toEqual([{
|
|
160
|
+
expect(results).toEqual([{
|
|
161
|
+
name: 'personal', action: 'recloned', version: 'v1.0.0', disposition: 'installed', pin: '1.0.0', availableVersion: 'v1.1.0',
|
|
162
|
+
}]);
|
|
161
163
|
});
|
|
162
164
|
it('baseline sembrado se sincroniza por el mismo loop que cualquier registry', async () => {
|
|
163
165
|
const m = require('../../src/core/registries');
|
|
@@ -167,7 +169,7 @@ describe('syncRegistries (git fixtures locales)', () => {
|
|
|
167
169
|
m.seedBaselineRegistry();
|
|
168
170
|
delete process.env.AWM_BASE_REMOTE;
|
|
169
171
|
const results = await m.syncRegistries();
|
|
170
|
-
expect(results).toEqual([{ name: 'baseline', action: 'recloned', version: 'v1.0.0' }]);
|
|
172
|
+
expect(results).toEqual([{ name: 'baseline', action: 'recloned', version: 'v1.0.0', disposition: 'installed', availableVersion: 'v1.0.0' }]);
|
|
171
173
|
expect(fs_1.default.existsSync(path_1.default.join(tmpHome, '.awm/registries/baseline/skills/alpha/SKILL.md'))).toBe(true);
|
|
172
174
|
});
|
|
173
175
|
it('verifyMinCliVersions reporta registries que exigen CLI más nuevo', async () => {
|
|
@@ -193,7 +195,7 @@ describe('syncRegistries (git fixtures locales)', () => {
|
|
|
193
195
|
m.writeRegistriesConfig([{ name: 'personal', remote: source }]);
|
|
194
196
|
// primera sync: queda en v1.0.0
|
|
195
197
|
const r1 = await m.syncRegistries();
|
|
196
|
-
expect(r1).toEqual([{ name: 'personal', action: 'recloned', version: 'v1.0.0' }]);
|
|
198
|
+
expect(r1).toEqual([{ name: 'personal', action: 'recloned', version: 'v1.0.0', disposition: 'installed', availableVersion: 'v1.0.0' }]);
|
|
197
199
|
// nueva release en el remote
|
|
198
200
|
fs_1.default.writeFileSync(path_1.default.join(source, 'skills', 'alpha', 'SKILL.md'), '---\nname: alpha\ndescription: v2\n---\n');
|
|
199
201
|
GIT(source, 'add -A');
|
|
@@ -201,9 +203,15 @@ describe('syncRegistries (git fixtures locales)', () => {
|
|
|
201
203
|
GIT(source, 'tag v1.1.0');
|
|
202
204
|
// segunda sync: avanza al nuevo tag
|
|
203
205
|
const r2 = await m.syncRegistries();
|
|
204
|
-
expect(r2).toEqual([{
|
|
206
|
+
expect(r2).toEqual([{
|
|
207
|
+
name: 'personal', action: 'pulled', version: 'v1.1.0', disposition: 'advanced', previousVersion: 'v1.0.0', availableVersion: 'v1.1.0',
|
|
208
|
+
}]);
|
|
205
209
|
const content = fs_1.default.readFileSync(path_1.default.join(tmpHome, '.awm/registries/personal/skills/alpha/SKILL.md'), 'utf-8');
|
|
206
210
|
expect(content).toContain('v2');
|
|
211
|
+
const r3 = await m.syncRegistries();
|
|
212
|
+
expect(r3).toEqual([{
|
|
213
|
+
name: 'personal', action: 'pulled', version: 'v1.1.0', disposition: 'unchanged', previousVersion: 'v1.1.0', availableVersion: 'v1.1.0',
|
|
214
|
+
}]);
|
|
207
215
|
});
|
|
208
216
|
it('rollback tag→tag: clone en v1.1.0 retrocede a v1.0.0 al establecer pin', async () => {
|
|
209
217
|
const m = require('../../src/core/registries');
|
|
@@ -216,13 +224,21 @@ describe('syncRegistries (git fixtures locales)', () => {
|
|
|
216
224
|
m.writeRegistriesConfig([{ name: 'personal', remote: source }]);
|
|
217
225
|
// primera sync sin pin: queda en v1.1.0
|
|
218
226
|
const r1 = await m.syncRegistries();
|
|
219
|
-
expect(r1).toEqual([{ name: 'personal', action: 'recloned', version: 'v1.1.0' }]);
|
|
227
|
+
expect(r1).toEqual([{ name: 'personal', action: 'recloned', version: 'v1.1.0', disposition: 'installed', availableVersion: 'v1.1.0' }]);
|
|
220
228
|
// establece pin a la versión anterior
|
|
221
229
|
const awmDir = path_1.default.join(tmpHome, '.awm');
|
|
222
230
|
fs_1.default.writeFileSync(path_1.default.join(awmDir, 'preferences.json'), JSON.stringify({ defaultAgent: 'claude-code', installMethod: 'symlink', defaultScope: 'local', pins: { personal: '1.0.0' } }));
|
|
223
231
|
// segunda sync con pin: retrocede a v1.0.0
|
|
224
232
|
const r2 = await m.syncRegistries();
|
|
225
|
-
expect(r2).toEqual([{
|
|
233
|
+
expect(r2).toEqual([{
|
|
234
|
+
name: 'personal',
|
|
235
|
+
action: 'pulled',
|
|
236
|
+
version: 'v1.0.0',
|
|
237
|
+
disposition: 'regressed',
|
|
238
|
+
pin: '1.0.0',
|
|
239
|
+
previousVersion: 'v1.1.0',
|
|
240
|
+
availableVersion: 'v1.1.0',
|
|
241
|
+
}]);
|
|
226
242
|
const content = fs_1.default.readFileSync(path_1.default.join(tmpHome, '.awm/registries/personal/skills/alpha/SKILL.md'), 'utf-8');
|
|
227
243
|
expect(content).toContain('test skill'); // contenido original del tag v1.0.0
|
|
228
244
|
});
|
|
@@ -236,14 +252,16 @@ describe('syncRegistries (git fixtures locales)', () => {
|
|
|
236
252
|
fs_1.default.writeFileSync(path_1.default.join(awmDir, 'preferences.json'), JSON.stringify({ defaultAgent: 'claude-code', installMethod: 'symlink', defaultScope: 'local', channel: 'dev' }));
|
|
237
253
|
// primera sync en canal dev: queda en HEAD (no en el tag)
|
|
238
254
|
const r1 = await m.syncRegistries();
|
|
239
|
-
expect(r1).toEqual([{ name: 'personal', action: 'recloned', version: 'HEAD' }]);
|
|
255
|
+
expect(r1).toEqual([{ name: 'personal', action: 'recloned', version: 'HEAD', disposition: 'installed' }]);
|
|
240
256
|
// nuevo commit en el remote (post-tag, no release)
|
|
241
257
|
fs_1.default.writeFileSync(path_1.default.join(source, 'skills', 'alpha', 'SKILL.md'), '---\nname: alpha\ndescription: head-2\n---\n');
|
|
242
258
|
GIT(source, 'add -A');
|
|
243
259
|
GIT(source, 'commit -qm head-2');
|
|
244
260
|
// segunda sync: recibe el nuevo commit
|
|
245
261
|
const r2 = await m.syncRegistries();
|
|
246
|
-
expect(r2).toEqual([{
|
|
262
|
+
expect(r2).toEqual([{
|
|
263
|
+
name: 'personal', action: 'pulled', version: 'HEAD', disposition: 'resolved', previousVersion: 'v1.0.0',
|
|
264
|
+
}]);
|
|
247
265
|
const content = fs_1.default.readFileSync(path_1.default.join(tmpHome, '.awm/registries/personal/skills/alpha/SKILL.md'), 'utf-8');
|
|
248
266
|
expect(content).toContain('head-2');
|
|
249
267
|
});
|
|
@@ -62,16 +62,16 @@ describe('versioning core', () => {
|
|
|
62
62
|
const clone = cloneOf(source, tmpWork, 'clone');
|
|
63
63
|
const { resolveTargetRef } = require('../../src/core/versioning');
|
|
64
64
|
const r = await resolveTargetRef(clone, { channel: 'stable' });
|
|
65
|
-
expect(r).toEqual({ kind: 'tag', ref: 'v1.10.0', version: '1.10.0' });
|
|
65
|
+
expect(r).toEqual({ kind: 'tag', ref: 'v1.10.0', version: '1.10.0', latestVersion: '1.10.0' });
|
|
66
66
|
});
|
|
67
67
|
it('pin exacto gana, con y sin prefijo v', async () => {
|
|
68
68
|
const source = makeTaggedRepo(tmpWork, 'src', ['1.0.0', '1.1.0']);
|
|
69
69
|
const clone = cloneOf(source, tmpWork, 'clone');
|
|
70
70
|
const { resolveTargetRef } = require('../../src/core/versioning');
|
|
71
71
|
expect(await resolveTargetRef(clone, { pin: '1.0.0', channel: 'stable' }))
|
|
72
|
-
.toEqual({ kind: 'tag', ref: 'v1.0.0', version: '1.0.0' });
|
|
72
|
+
.toEqual({ kind: 'tag', ref: 'v1.0.0', version: '1.0.0', latestVersion: '1.1.0' });
|
|
73
73
|
expect(await resolveTargetRef(clone, { pin: 'v1.0.0', channel: 'stable' }))
|
|
74
|
-
.toEqual({ kind: 'tag', ref: 'v1.0.0', version: '1.0.0' });
|
|
74
|
+
.toEqual({ kind: 'tag', ref: 'v1.0.0', version: '1.0.0', latestVersion: '1.1.0' });
|
|
75
75
|
});
|
|
76
76
|
it('pin inexistente → error que lista las versiones disponibles', async () => {
|
|
77
77
|
const source = makeTaggedRepo(tmpWork, 'src', ['1.0.0']);
|
|
@@ -102,7 +102,7 @@ describe('versioning core', () => {
|
|
|
102
102
|
const clone = cloneOf(source, tmpWork, 'clone');
|
|
103
103
|
const { resolveTargetRef } = require('../../src/core/versioning');
|
|
104
104
|
const r = await resolveTargetRef(clone, { channel: 'stable' });
|
|
105
|
-
expect(r).toEqual({ kind: 'tag', ref: 'v1.0.0', version: '1.0.0' });
|
|
105
|
+
expect(r).toEqual({ kind: 'tag', ref: 'v1.0.0', version: '1.0.0', latestVersion: '1.0.0' });
|
|
106
106
|
});
|
|
107
107
|
it('hace fetch: ve tags creados en el remote después del clone', async () => {
|
|
108
108
|
const source = makeTaggedRepo(tmpWork, 'src', ['1.0.0']);
|
|
@@ -114,7 +114,7 @@ describe('versioning core', () => {
|
|
|
114
114
|
GIT(source, 'tag v1.1.0');
|
|
115
115
|
const { resolveTargetRef } = require('../../src/core/versioning');
|
|
116
116
|
const r = await resolveTargetRef(clone, { channel: 'stable' });
|
|
117
|
-
expect(r).toEqual({ kind: 'tag', ref: 'v1.1.0', version: '1.1.0' });
|
|
117
|
+
expect(r).toEqual({ kind: 'tag', ref: 'v1.1.0', version: '1.1.0', latestVersion: '1.1.0' });
|
|
118
118
|
});
|
|
119
119
|
});
|
|
120
120
|
describe('currentVersion', () => {
|