agentic-workflow-manager 9.11.9 → 9.11.11

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.
@@ -51,6 +51,18 @@ function authoritativeSource(value) {
51
51
  function sanitizeSource(value) {
52
52
  return authoritativeSource(value) ?? '[configured remote]';
53
53
  }
54
+ function sshUserInfoLabel(value) {
55
+ try {
56
+ const url = new URL(value);
57
+ if (url.protocol === 'ssh:')
58
+ return url.username ? 'SSH userinfo present' : 'no SSH userinfo';
59
+ }
60
+ catch {
61
+ if (/^[A-Za-z0-9._-]+@[A-Za-z0-9.-]+:[A-Za-z0-9._/-]+$/.test(value))
62
+ return 'SSH userinfo present';
63
+ }
64
+ return 'no SSH userinfo';
65
+ }
54
66
  function parseVersion(value) {
55
67
  const match = STRICT_SEMVER.exec(value.trim());
56
68
  if (!match)
@@ -144,33 +156,40 @@ async function registryComponent(registry, git, prefs, checkedAt) {
144
156
  const authorizedSource = authoritativeSource(registry.remote);
145
157
  const source = authorizedSource ?? '[configured remote]';
146
158
  const pin = prefs.pins?.[registry.name];
159
+ const fail = (detail, remedy, installed = null, latest = null) => ({
160
+ ...unavailable(`registry:${registry.name}`, source, checkedAt, installed, pin), latest, detail, remedy,
161
+ });
147
162
  if (!authorizedSource)
148
- return unavailable(`registry:${registry.name}`, source, checkedAt, null, pin);
149
- let origin;
150
- let head;
151
- let exactTag;
152
- let remoteTags;
153
- try {
154
- [origin, head, exactTag, remoteTags] = await Promise.all([
155
- deadline(git(registry.contentRoot, ['remote', 'get-url', 'origin'], { timeoutMs: TIMEOUT_MS, maxOutputBytes: MAX_OUTPUT_BYTES })),
156
- deadline(git(registry.contentRoot, ['rev-parse', 'HEAD'], { timeoutMs: TIMEOUT_MS, maxOutputBytes: MAX_OUTPUT_BYTES })),
157
- deadline(git(registry.contentRoot, ['describe', '--tags', '--exact-match', 'HEAD'], { timeoutMs: TIMEOUT_MS, maxOutputBytes: MAX_OUTPUT_BYTES })),
158
- deadline(git(registry.contentRoot, ['ls-remote', '--tags', registry.remote], { timeoutMs: TIMEOUT_MS, maxOutputBytes: MAX_OUTPUT_BYTES })),
159
- ]);
160
- }
161
- catch {
162
- return unavailable(`registry:${registry.name}`, source, checkedAt, null, pin);
163
- }
164
- const installed = exactTag.trim();
165
- const latest = latestStableTag(remoteTags);
166
- const localHead = head.trim();
163
+ return fail('Configured registry remote is not an authoritative HTTPS or SSH source.', 'Configure an authoritative registry remote and rerun strict preflight.');
164
+ const [originResult, headResult, tagResult, remoteResult] = await Promise.allSettled([
165
+ deadline(git(registry.contentRoot, ['remote', 'get-url', 'origin'], { timeoutMs: TIMEOUT_MS, maxOutputBytes: MAX_OUTPUT_BYTES })),
166
+ deadline(git(registry.contentRoot, ['rev-parse', 'HEAD'], { timeoutMs: TIMEOUT_MS, maxOutputBytes: MAX_OUTPUT_BYTES })),
167
+ deadline(git(registry.contentRoot, ['describe', '--tags', '--exact-match', 'HEAD'], { timeoutMs: TIMEOUT_MS, maxOutputBytes: MAX_OUTPUT_BYTES })),
168
+ deadline(git(registry.contentRoot, ['ls-remote', '--tags', registry.remote], { timeoutMs: TIMEOUT_MS, maxOutputBytes: MAX_OUTPUT_BYTES })),
169
+ ]);
170
+ if (originResult.status === 'rejected')
171
+ return fail('Could not read local registry origin.', 'Inspect the local registry checkout and its origin remote.');
172
+ if (headResult.status === 'rejected')
173
+ return fail('Could not read local registry HEAD.', 'Inspect or repair the local registry checkout.');
174
+ if (tagResult.status === 'rejected')
175
+ return fail('Could not read an exact tag at local registry HEAD.', 'Check out a stable tag in the local registry and rerun strict preflight.');
176
+ if (remoteResult.status === 'rejected')
177
+ return fail('Could not query authoritative remote tags.', 'Restore source access and rerun strict preflight.');
178
+ const installed = tagResult.value.trim();
179
+ const latest = latestStableTag(remoteResult.value);
180
+ const localHead = headResult.value.trim();
167
181
  const installedVersion = parseVersion(installed);
168
- if (!installedVersion || !latest || !/^[0-9a-fA-F]{40,64}$/.test(localHead) || origin.trim() !== registry.remote) {
169
- return unavailable(`registry:${registry.name}`, source, checkedAt, installedVersion ? installed : null, pin);
170
- }
182
+ if (!installedVersion)
183
+ return fail('Local registry HEAD does not have an exact stable tag.', 'Check out a stable tag in the local registry and rerun strict preflight.');
184
+ if (!latest)
185
+ return fail('No stable version tag was found on the authoritative remote.', 'Publish a stable tag or correct the configured registry remote.', installed);
186
+ if (!/^[0-9a-fA-F]{40,64}$/.test(localHead))
187
+ return fail('Local registry HEAD is not a valid commit SHA.', 'Inspect or repair the local registry checkout.', installed, latest.tag);
188
+ if (originResult.value.trim() !== registry.remote)
189
+ return fail(`Local origin does not match configured remote (origin: ${sanitizeSource(originResult.value.trim())} [${sshUserInfoLabel(originResult.value.trim())}]; configured: ${source} [${sshUserInfoLabel(registry.remote)}]).`, 'Align the local origin with the configured registry remote and rerun strict preflight.', installed, latest.tag);
171
190
  const relation = compareStrict(installed, latest.tag);
172
191
  if (relation === null)
173
- return unavailable(`registry:${registry.name}`, source, checkedAt, installed, pin);
192
+ return fail('Stable registry tags could not be compared.', 'Inspect local and remote stable tags.', installed, latest.tag);
174
193
  if (relation === 0 && localHead === latest.sha) {
175
194
  return { component: `registry:${registry.name}`, installed, latest: latest.tag, channel: 'stable', source, ...(pin ? { pin } : {}), checkedAt, status: 'current', detail: 'Exact stable tag and configured origin match the authoritative remote.', remedy: 'No action required.' };
176
195
  }
@@ -181,7 +200,7 @@ async function registryComponent(registry, git, prefs, checkedAt) {
181
200
  remedy: pin ? `awm unpin ${registry.name} && awm update --yes` : 'awm update --yes',
182
201
  };
183
202
  }
184
- return unavailable(`registry:${registry.name}`, source, checkedAt, installed, pin);
203
+ return fail('Installed stable tag is ahead of the authoritative remote or does not match its commit.', 'Inspect the local tag and authoritative remote before updating.', installed, latest.tag);
185
204
  }
186
205
  /** Strict, read-only remote currentness check. It intentionally never reads/writes update cache. */
187
206
  async function checkCurrentness(cwd, deps = {}) {
@@ -195,11 +214,14 @@ async function checkCurrentness(cwd, deps = {}) {
195
214
  const preferences = (deps.readPreferences ?? config_1.readPreferences)();
196
215
  const installedCli = getCliVersion();
197
216
  const latest = await latestCli(fetchImpl);
198
- const cli = !parseVersion(installedCli) || !latest || compareStrict(installedCli, latest) === null
217
+ const relation = latest ? compareStrict(installedCli, latest) : null;
218
+ const cli = relation === null
199
219
  ? unavailable('cli', NPM_SOURCE, checkedAt, parseVersion(installedCli) ? installedCli : null)
200
- : compareStrict(installedCli, latest) === 0
220
+ : relation === 0
201
221
  ? { component: 'cli', installed: installedCli, latest, channel: 'stable', source: NPM_SOURCE, checkedAt, status: 'current', detail: 'Installed version equals the npm latest release.', remedy: 'No action required.' }
202
- : { component: 'cli', installed: installedCli, latest, channel: 'stable', source: NPM_SOURCE, checkedAt, status: 'stale', detail: 'Installed version is behind the npm latest release.', remedy: `npm i -g ${cli_version_1.CLI_PACKAGE_NAME}@latest && rerun in a fresh process` };
222
+ : relation < 0
223
+ ? { component: 'cli', installed: installedCli, latest, channel: 'stable', source: NPM_SOURCE, checkedAt, status: 'stale', detail: 'Installed version is behind the npm latest release.', remedy: `npm i -g ${cli_version_1.CLI_PACKAGE_NAME}@latest && rerun in a fresh process` }
224
+ : { component: 'cli', installed: installedCli, latest, channel: 'stable', source: NPM_SOURCE, checkedAt, status: 'unverifiable', detail: 'Installed version is ahead of the npm latest release; remote currentness cannot be established.', remedy: 'Check the installed CLI version and npm latest metadata, then rerun strict preflight.' };
203
225
  const components = [cli];
204
226
  let registries;
205
227
  try {
@@ -97,6 +97,19 @@ describe('checkCurrentness', () => {
97
97
  status: 'stale',
98
98
  }));
99
99
  });
100
+ it('does not claim a CLI ahead of npm latest is stale or recommend a downgrade', async () => {
101
+ const { checkCurrentness } = require('../../../src/core/currentness/check');
102
+ const result = await checkCurrentness(root, deps({
103
+ cliVersion: () => '1.2.4',
104
+ }));
105
+ expect(result.components[0]).toEqual(expect.objectContaining({
106
+ installed: '1.2.4',
107
+ latest: '1.2.3',
108
+ status: 'unverifiable',
109
+ detail: expect.stringMatching(/ahead.*npm latest/i),
110
+ }));
111
+ expect(result.components[0].remedy).not.toContain('npm i -g');
112
+ });
100
113
  it('marks a registry tag above the max-safe numeric boundary as stale', async () => {
101
114
  const installed = 'v1.9007199254740992.0';
102
115
  const latest = 'v1.9007199254740993.0';
@@ -165,6 +178,106 @@ describe('checkCurrentness', () => {
165
178
  : { git }));
166
179
  expect(result.components.some((c) => c.status === 'unverifiable')).toBe(true);
167
180
  });
181
+ it('identifies missing exact-tag provenance without blaming source access', async () => {
182
+ const { checkCurrentness } = require('../../../src/core/currentness/check');
183
+ const result = await checkCurrentness(root, deps({
184
+ git: jest.fn().mockImplementation(async (_cwd, args) => {
185
+ if (args[0] === 'describe')
186
+ throw new Error('fatal: no tag exactly matches HEAD');
187
+ if (args[0] === 'ls-remote')
188
+ return `${'a'.repeat(40)}\trefs/tags/v1.2.3\n`;
189
+ if (args[0] === 'remote')
190
+ return 'https://example.test/team/repo.git\n';
191
+ return `${'a'.repeat(40)}\n`;
192
+ }),
193
+ }));
194
+ expect(result.components[1]).toEqual(expect.objectContaining({
195
+ status: 'unverifiable',
196
+ detail: expect.stringMatching(/exact.*tag.*HEAD/i),
197
+ remedy: expect.stringMatching(/stable tag/i),
198
+ }));
199
+ expect(result.components[1].remedy).not.toContain('Restore source access');
200
+ });
201
+ it('identifies origin mismatch while keeping credential-bearing remotes out of diagnostics', async () => {
202
+ const { checkCurrentness } = require('../../../src/core/currentness/check');
203
+ const result = await checkCurrentness(root, deps({
204
+ git: jest.fn().mockImplementation(async (_cwd, args) => {
205
+ if (args[0] === 'remote')
206
+ return 'https://user:secret@example.test/team/other.git\n';
207
+ if (args[0] === 'ls-remote')
208
+ return `${'a'.repeat(40)}\trefs/tags/v1.2.3\n`;
209
+ if (args[0] === 'describe')
210
+ return 'v1.2.3\n';
211
+ return `${'a'.repeat(40)}\n`;
212
+ }),
213
+ }));
214
+ expect(result.components[1]).toEqual(expect.objectContaining({
215
+ status: 'unverifiable',
216
+ detail: expect.stringMatching(/origin.*configured remote/i),
217
+ remedy: expect.stringMatching(/origin/i),
218
+ }));
219
+ expect(JSON.stringify(result)).not.toContain('secret');
220
+ expect(JSON.stringify(result)).not.toContain('user:');
221
+ });
222
+ it('makes an SSH-user-only origin mismatch visible after sanitizing both remotes', async () => {
223
+ const configured = 'ssh://github.example/team/repo.git';
224
+ const { checkCurrentness } = require('../../../src/core/currentness/check');
225
+ const result = await checkCurrentness(root, deps({
226
+ listRegistries: () => [{ name: 'baseline', remote: configured, contentRoot: '/registry' }],
227
+ git: jest.fn().mockImplementation(async (_cwd, args) => {
228
+ if (args[0] === 'remote')
229
+ return 'ssh://git@github.example/team/repo.git\n';
230
+ if (args[0] === 'ls-remote')
231
+ return `${'a'.repeat(40)}\trefs/tags/v1.2.3\n`;
232
+ if (args[0] === 'describe')
233
+ return 'v1.2.3\n';
234
+ return `${'a'.repeat(40)}\n`;
235
+ }),
236
+ }));
237
+ expect(result.components[1]).toEqual(expect.objectContaining({
238
+ status: 'unverifiable',
239
+ detail: expect.stringMatching(/origin.*SSH userinfo present.*configured.*no SSH userinfo/i),
240
+ }));
241
+ expect(JSON.stringify(result)).not.toContain('git@');
242
+ });
243
+ it('distinguishes a remote tag query failure from local registry failures', async () => {
244
+ const { checkCurrentness } = require('../../../src/core/currentness/check');
245
+ const result = await checkCurrentness(root, deps({
246
+ git: jest.fn().mockImplementation(async (_cwd, args) => {
247
+ if (args[0] === 'ls-remote')
248
+ throw new Error('remote unavailable');
249
+ if (args[0] === 'remote')
250
+ return 'https://example.test/team/repo.git\n';
251
+ if (args[0] === 'describe')
252
+ return 'v1.2.3\n';
253
+ return `${'a'.repeat(40)}\n`;
254
+ }),
255
+ }));
256
+ expect(result.components[1]).toEqual(expect.objectContaining({
257
+ status: 'unverifiable',
258
+ detail: expect.stringMatching(/remote.*tags/i),
259
+ remedy: expect.stringMatching(/source access/i),
260
+ }));
261
+ });
262
+ it('reports a remote with no stable tags as a tag problem', async () => {
263
+ const { checkCurrentness } = require('../../../src/core/currentness/check');
264
+ const result = await checkCurrentness(root, deps({
265
+ git: jest.fn().mockImplementation(async (_cwd, args) => {
266
+ if (args[0] === 'ls-remote')
267
+ return `${'a'.repeat(40)}\trefs/tags/v1.2.4-rc.1\n`;
268
+ if (args[0] === 'remote')
269
+ return 'https://example.test/team/repo.git\n';
270
+ if (args[0] === 'describe')
271
+ return 'v1.2.3\n';
272
+ return `${'a'.repeat(40)}\n`;
273
+ }),
274
+ }));
275
+ expect(result.components[1]).toEqual(expect.objectContaining({
276
+ status: 'unverifiable',
277
+ detail: expect.stringMatching(/no stable.*tag/i),
278
+ remedy: expect.stringMatching(/stable tag/i),
279
+ }));
280
+ });
168
281
  it('bypasses passive update controls and makes no writes', async () => {
169
282
  const { checkCurrentness } = require('../../../src/core/currentness/check');
170
283
  const before = fs_1.default.readdirSync(root);
@@ -8,21 +8,90 @@ const fs_1 = __importDefault(require("fs"));
8
8
  const path_1 = __importDefault(require("path"));
9
9
  const CLI_ROOT = path_1.default.resolve(__dirname, '..', '..');
10
10
  const REPO_ROOT = path_1.default.resolve(CLI_ROOT, '..');
11
+ function readWorkflow(name) {
12
+ return fs_1.default.readFileSync(path_1.default.join(REPO_ROOT, '.github', 'workflows', name), 'utf8').replace(/\r\n/g, '\n');
13
+ }
14
+ function assertArmPartition(source) {
15
+ const workflow = source.replace(/\r\n/g, '\n');
16
+ expect(workflow.match(/^ windows-arm-hot:\s*$/gm)).toHaveLength(1);
17
+ const [matrixJob, rest] = workflow.split(/^ windows-arm-hot:\s*$/m);
18
+ expect(rest).toBeDefined();
19
+ const hotJob = rest.split(/^ [a-z][\w-]*:\s*$/m)[0];
20
+ expect(matrixJob).toMatch(/- name: Tests?\n if: matrix\.target != 'win32-arm64'\n working-directory: cli\n run: npx jest --runInBand --bail\n/);
21
+ const remainder = matrixJob.match(/- name: Tests? \(Windows ARM remainder\)\n if: matrix\.target == 'win32-arm64'\n working-directory: cli\n run: ([^\n]+)/)?.[1];
22
+ expect(remainder).toMatch(/^npx jest --runInBand --bail --testPathIgnorePatterns='[^']+'$/);
23
+ const ignorePattern = remainder?.match(/--testPathIgnorePatterns='([^']+)'/)?.[1];
24
+ expect(ignorePattern).toBe(String.raw `node_modules|[/\\]cli[/\\]tests[/\\]commands[/\\]watch[/\\]track-(finalize|freeze)\.test\.ts$`);
25
+ const ignored = new RegExp(ignorePattern);
26
+ for (const separator of ['/', '\\']) {
27
+ const root = ['repo', 'cli', 'tests'].join(separator);
28
+ for (const suite of ['track-finalize', 'track-freeze']) {
29
+ expect(ignored.test([root, 'commands', 'watch', `${suite}.test.ts`].join(separator))).toBe(true);
30
+ expect(ignored.test([root, 'other', `${suite}.test.ts`].join(separator))).toBe(false);
31
+ }
32
+ }
33
+ const remainderStep = matrixJob.split(/- name: Tests? \(Windows ARM remainder\)\n/)[1]?.split(/^ - name:/m)[0];
34
+ expect(remainderStep).toBeDefined();
35
+ expect(remainderStep).not.toContain('continue-on-error: true');
36
+ expect(matrixJob.match(/uses: actions\/upload-artifact@v4/g)).toHaveLength(1);
37
+ expect(hotJob).toContain('runs-on: windows-11-arm');
38
+ expect(hotJob).toContain('run: npm run native:build');
39
+ expect(hotJob).toContain('run: npm run native:test-build');
40
+ expect(hotJob.split('\n')).toContain(' run: npx jest --runTestsByPath tests/commands/watch/track-finalize.test.ts tests/commands/watch/track-freeze.test.ts --runInBand --bail');
41
+ expect(hotJob).not.toContain('upload-artifact@v4');
42
+ expect(hotJob).not.toContain('secure-fs-win32-arm64');
43
+ expect(hotJob).not.toMatch(/^\s*if:/m);
44
+ expect(hotJob).not.toContain('continue-on-error: true');
45
+ }
11
46
  describe('native release artifacts', () => {
12
47
  it('runs each platform suite once without weakening publication', () => {
13
48
  for (const name of ['ci.yml', 'release.yml']) {
14
- const workflow = fs_1.default.readFileSync(path_1.default.join(REPO_ROOT, '.github', 'workflows', name), 'utf8');
49
+ const workflow = readWorkflow(name);
15
50
  expect(workflow).not.toContain('- name: Windows admission smoke');
16
51
  expect(workflow).not.toContain('run: npx jest tests/commands/plan/index.test.ts --runInBand --bail');
17
52
  expect(workflow).toContain('run: npx jest --runInBand --bail');
18
53
  }
19
- const release = fs_1.default.readFileSync(path_1.default.join(REPO_ROOT, '.github', 'workflows', 'release.yml'), 'utf8');
20
- expect(release).toMatch(/release:\s*\n\s*needs: test/);
54
+ const release = readWorkflow('release.yml');
55
+ expect(release).toMatch(/release:\s*\n\s*needs: \[test, windows-arm-hot\]/);
21
56
  for (const target of ['linux-x64', 'linux-arm64', 'darwin-x64', 'darwin-arm64', 'win32-x64', 'win32-arm64']) {
22
57
  expect(release).toContain(`target: ${target}`);
23
58
  }
24
59
  expect(release).toContain('cancel-in-progress: false');
25
60
  });
61
+ it('partitions the two hot ARM suites without duplicating the native artifact', () => {
62
+ for (const name of ['ci.yml', 'release.yml']) {
63
+ const workflow = readWorkflow(name);
64
+ assertArmPartition(workflow);
65
+ }
66
+ });
67
+ it('checks the same partition after a Windows CRLF checkout', () => {
68
+ for (const name of ['ci.yml', 'release.yml']) {
69
+ const workflow = readWorkflow(name);
70
+ assertArmPartition(workflow.replace(/\n/g, '\r\n'));
71
+ }
72
+ });
73
+ it('rejects swapped matrix guards, non-gating hot tests and duplicate artifacts', () => {
74
+ for (const name of ['ci.yml', 'release.yml']) {
75
+ const workflow = readWorkflow(name);
76
+ const swapped = workflow.replace(/if: matrix\.target (?:!=|==) 'win32-arm64'/g, (guard) => guard.includes('!=') ? "if: matrix.target == 'win32-arm64'" : "if: matrix.target != 'win32-arm64'");
77
+ const nonGating = workflow.replace(' windows-arm-hot:\n', ' windows-arm-hot:\n continue-on-error: true\n');
78
+ const duplicateArtifact = workflow.replace('uses: actions/upload-artifact@v4', 'uses: actions/upload-artifact@v4\n - uses: actions/upload-artifact@v4');
79
+ const broadIgnore = workflow.replace(/(--testPathIgnorePatterns='[^']+)(?=')/, '$1|runner\\.test\\.ts$');
80
+ const skippedHot = workflow.replace(' - name: Test hot Windows ARM suites\n', ' - name: Test hot Windows ARM suites\n if: false\n');
81
+ const nonGatingRemainder = workflow.replace(/(run: npx jest --runInBand --bail --testPathIgnorePatterns='[^']+'\n)/, '$1 continue-on-error: true\n');
82
+ const maskedHot = workflow.replace(/(run: npx jest --runTestsByPath [^\n]+ --runInBand --bail)(?=\n)/, '$1; exit 0');
83
+ for (const mutated of [swapped, nonGating, duplicateArtifact, broadIgnore, skippedHot, nonGatingRemainder, maskedHot]) {
84
+ expect(mutated).not.toBe(workflow);
85
+ }
86
+ expect(() => assertArmPartition(swapped)).toThrow();
87
+ expect(() => assertArmPartition(nonGating)).toThrow();
88
+ expect(() => assertArmPartition(duplicateArtifact)).toThrow();
89
+ expect(() => assertArmPartition(broadIgnore)).toThrow();
90
+ expect(() => assertArmPartition(skippedHot)).toThrow();
91
+ expect(() => assertArmPartition(nonGatingRemainder)).toThrow();
92
+ expect(() => assertArmPartition(maskedHot)).toThrow();
93
+ }
94
+ });
26
95
  it('keeps native prebuilds generated instead of versioned source', () => {
27
96
  const gitignore = fs_1.default.readFileSync(path_1.default.join(REPO_ROOT, '.gitignore'), 'utf8');
28
97
  const tracked = (0, child_process_1.execFileSync)('git', ['ls-files', '--', 'cli/prebuilds'], {
@@ -37,7 +106,7 @@ describe('native release artifacts', () => {
37
106
  const pkg = JSON.parse(fs_1.default.readFileSync(path_1.default.join(CLI_ROOT, 'package.json'), 'utf8'));
38
107
  expect(pkg.scripts['native:test-build']).toContain('secure_fs_test');
39
108
  for (const name of ['ci.yml', 'release.yml']) {
40
- const workflow = fs_1.default.readFileSync(path_1.default.join(REPO_ROOT, '.github', 'workflows', name), 'utf8');
109
+ const workflow = readWorkflow(name);
41
110
  const testBuild = workflow.indexOf('run: npm run native:test-build');
42
111
  const jest = workflow.indexOf('run: npx jest --runInBand --bail');
43
112
  expect(testBuild).toBeGreaterThanOrEqual(0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentic-workflow-manager",
3
- "version": "9.11.9",
3
+ "version": "9.11.11",
4
4
  "main": "dist/src/index.js",
5
5
  "bin": {
6
6
  "awm": "./dist/src/index.js"
Binary file
Binary file