agentworkforce 4.1.27 → 4.1.30

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/CHANGELOG.md CHANGED
@@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [4.1.29] - 2026-07-17
11
+
12
+ ### Fixed
13
+
14
+ - Select a newer coherent project-local `agentworkforce` installation ahead of
15
+ an older global one, while refusing partial or version-skewed wrapper/CLI
16
+ pairs. Version output now validates the exact implementation it represents
17
+ (#163).
18
+
10
19
  ## [4.1.18] - 2026-07-15
11
20
 
12
21
  ### Added
package/README.md CHANGED
@@ -22,6 +22,17 @@ This package is a thin wrapper around [`@agentworkforce/cli`](https://www.npmjs.
22
22
  It exists so the global install command and the binary name match the
23
23
  project name.
24
24
 
25
+ The wrapper validates that its associated `@agentworkforce/cli` has the same
26
+ version before running it. When the current project has a newer local
27
+ `agentworkforce` install, the wrapper uses that install only when its wrapper
28
+ and associated CLI versions also match. Partial installs fail with a copyable
29
+ reinstall command instead of silently executing a stale nested CLI.
30
+ An already-running pre-fix wrapper cannot use this selection logic, so users on
31
+ 4.1.25 must reinstall or update `agentworkforce` before the fix can take effect.
32
+ The fixed wrapper does not query a registry for the latest release, so without a
33
+ newer coherent local install it cannot detect that its own coherent version is
34
+ stale relative to a future release; updating or reinstalling remains required.
35
+
25
36
  See the [main README](https://github.com/AgentWorkforce/workforce#readme)
26
37
  for the full feature tour, and [`packages/cli/README.md`](https://github.com/AgentWorkforce/workforce/blob/main/packages/cli/README.md)
27
38
  for command reference.
@@ -1,30 +1,242 @@
1
1
  #!/usr/bin/env node
2
- import { readFileSync } from 'node:fs';
2
+ import { existsSync, readFileSync } from 'node:fs';
3
+ import { createRequire } from 'node:module';
4
+ import path from 'node:path';
5
+ import { fileURLToPath, pathToFileURL } from 'node:url';
6
+
7
+ const require = createRequire(import.meta.url);
8
+
9
+ class InstallationError extends Error {}
3
10
 
4
11
  // Top-level wrapper. Delegates to the @agentworkforce/cli entry point under the
5
12
  // shorter `agentworkforce` bin name so users can `npm i -g agentworkforce` and
6
13
  // run `agentworkforce agent <persona>`. The CLI derives its help-text bin name
7
14
  // from process.argv[1], so this file's basename (sans extension) is what
8
15
  // shows up in usage strings.
9
- function readPackageVersion() {
10
- const pkg = JSON.parse(
11
- readFileSync(new URL('../package.json', import.meta.url), 'utf8')
12
- );
16
+ function readPackage(packageJson, label) {
17
+ let pkg;
18
+ try {
19
+ pkg = JSON.parse(readFileSync(packageJson, 'utf8'));
20
+ } catch {
21
+ throw new InstallationError(`${label} package metadata is invalid.`);
22
+ }
13
23
  if (typeof pkg.version !== 'string' || !pkg.version) {
14
- throw new Error('Could not read agentworkforce package version.');
24
+ throw new InstallationError(`Could not read ${label} package version.`);
15
25
  }
16
- return pkg.version;
26
+ return pkg;
27
+ }
28
+
29
+ function resolveCli() {
30
+ const wrapperPackageJsonUrl = new URL('../package.json', import.meta.url);
31
+ const wrapper = readPackage(
32
+ wrapperPackageJsonUrl,
33
+ 'agentworkforce'
34
+ );
35
+ parseVersion(wrapper.version, 'agentworkforce wrapper');
36
+ const project = resolveProjectInstall(
37
+ fileURLToPath(wrapperPackageJsonUrl),
38
+ wrapper.version
39
+ );
40
+
41
+ if (project) {
42
+ if (project.wrapperVersion !== project.cli.version) {
43
+ throw new InstallationError(
44
+ [
45
+ 'project-local agentworkforce installation is inconsistent; refusing to run it.',
46
+ `project wrapper version: ${project.wrapperVersion}`,
47
+ `project @agentworkforce/cli version: ${project.cli.version}`,
48
+ `Repair it with: npm install agentworkforce@${project.wrapperVersion}`
49
+ ].join('\n')
50
+ );
51
+ }
52
+ return { version: project.cli.version, entryUrl: project.cli.entryUrl };
53
+ }
54
+
55
+ // Only require the invoked wrapper's dependency after checking whether a
56
+ // newer coherent project install wins. A partial global install must not
57
+ // mask a complete, newer local candidate.
58
+ const bundledRepair = `npm install -g agentworkforce@${wrapper.version}`;
59
+ const bundled = readCliCandidate(
60
+ resolveAssociatedCliPackageJson(
61
+ require,
62
+ 'agentworkforce installation',
63
+ bundledRepair
64
+ ),
65
+ 'wrapper dependency',
66
+ bundledRepair
67
+ );
68
+
69
+ if (wrapper.version !== bundled.version) {
70
+ throw new InstallationError(
71
+ [
72
+ 'agentworkforce installation is inconsistent; refusing to run a stale nested CLI.',
73
+ `wrapper version: ${wrapper.version}`,
74
+ `resolved @agentworkforce/cli version: ${bundled.version}`,
75
+ `Repair it with: npm install -g agentworkforce@${wrapper.version}`
76
+ ].join('\n')
77
+ );
78
+ }
79
+
80
+ return {
81
+ version: bundled.version,
82
+ entryUrl: bundled.entryUrl
83
+ };
84
+ }
85
+
86
+ function resolveProjectInstall(invokedWrapperPackageJsonPath, invokedWrapperVersion) {
87
+ const projectRequire = createRequire(
88
+ pathToFileURL(path.join(process.cwd(), '__agentworkforce_resolve__.cjs'))
89
+ );
90
+ let wrapperPackageJsonPath;
91
+ try {
92
+ wrapperPackageJsonPath = projectRequire.resolve('agentworkforce/package.json');
93
+ } catch (error) {
94
+ if (error?.code === 'MODULE_NOT_FOUND') return undefined;
95
+ throw new InstallationError(
96
+ [
97
+ 'project-local agentworkforce package metadata cannot be inspected; refusing to run an unverified local installation.',
98
+ 'Repair it with: npm install agentworkforce'
99
+ ].join('\n')
100
+ );
101
+ }
102
+
103
+ if (path.resolve(wrapperPackageJsonPath) === path.resolve(invokedWrapperPackageJsonPath)) {
104
+ return undefined;
105
+ }
106
+
107
+ const projectWrapper = readPackage(wrapperPackageJsonPath, 'project-local agentworkforce');
108
+ // Validate candidate version syntax even when it would not win selection;
109
+ // a broken local launcher must not be silently mistaken for an older one.
110
+ parseVersion(projectWrapper.version, 'project-local agentworkforce');
111
+ if (compareVersions(projectWrapper.version, invokedWrapperVersion) <= 0) {
112
+ return undefined;
113
+ }
114
+ const localRequire = createRequire(pathToFileURL(wrapperPackageJsonPath));
115
+ const repairCommand = `npm install agentworkforce@${projectWrapper.version}`;
116
+ const cliPackageJsonPath = resolveAssociatedCliPackageJson(
117
+ localRequire,
118
+ 'project-local agentworkforce installation',
119
+ repairCommand
120
+ );
121
+ return {
122
+ wrapperVersion: projectWrapper.version,
123
+ cli: readCliCandidate(
124
+ cliPackageJsonPath,
125
+ 'project-local wrapper dependency',
126
+ repairCommand
127
+ )
128
+ };
129
+ }
130
+
131
+ function resolveAssociatedCliPackageJson(localRequire, installationLabel, repairCommand) {
132
+ try {
133
+ return localRequire.resolve('@agentworkforce/cli/package.json');
134
+ } catch {
135
+ throw new InstallationError(
136
+ [
137
+ `${installationLabel} has no resolvable @agentworkforce/cli metadata; refusing to run a partial or unverified installation.`,
138
+ `Repair it with: ${repairCommand}`
139
+ ].join('\n')
140
+ );
141
+ }
142
+ }
143
+
144
+ function readCliCandidate(packageJsonPath, source, repairCommand) {
145
+ const pkg = readPackage(packageJsonPath, `@agentworkforce/cli ${source}`);
146
+ parseVersion(pkg.version, `@agentworkforce/cli ${source}`);
147
+ const entryUrl = new URL('./dist/cli.js', pathToFileURL(packageJsonPath));
148
+ if (!existsSync(fileURLToPath(entryUrl))) {
149
+ throw new InstallationError(
150
+ [
151
+ `@agentworkforce/cli ${source} has no executable entry; refusing to run a partial installation.`,
152
+ `Repair it with: ${repairCommand}`
153
+ ].join('\n')
154
+ );
155
+ }
156
+ return {
157
+ version: pkg.version,
158
+ entryUrl: entryUrl.href
159
+ };
160
+ }
161
+
162
+ function compareVersions(left, right) {
163
+ const a = parseVersion(left);
164
+ const b = parseVersion(right);
165
+ for (let i = 0; i < 3; i += 1) {
166
+ if (a.numbers[i] < b.numbers[i]) return -1;
167
+ if (a.numbers[i] > b.numbers[i]) return 1;
168
+ }
169
+ if (a.prerelease === b.prerelease) return 0;
170
+ if (!a.prerelease) return 1;
171
+ if (!b.prerelease) return -1;
172
+ return comparePrerelease(a.prerelease, b.prerelease);
173
+ }
174
+
175
+ function comparePrerelease(left, right) {
176
+ const a = left.split('.');
177
+ const b = right.split('.');
178
+ const length = Math.max(a.length, b.length);
179
+ for (let i = 0; i < length; i += 1) {
180
+ if (a[i] === undefined) return -1;
181
+ if (b[i] === undefined) return 1;
182
+ if (a[i] === b[i]) continue;
183
+ const aNumeric = /^\d+$/.test(a[i]);
184
+ const bNumeric = /^\d+$/.test(b[i]);
185
+ if (aNumeric && bNumeric) {
186
+ const aNumber = BigInt(a[i]);
187
+ const bNumber = BigInt(b[i]);
188
+ if (aNumber < bNumber) return -1;
189
+ if (aNumber > bNumber) return 1;
190
+ continue;
191
+ }
192
+ if (aNumeric) return -1;
193
+ if (bNumeric) return 1;
194
+ return a[i] < b[i] ? -1 : 1;
195
+ }
196
+ return 0;
197
+ }
198
+
199
+ function parseVersion(version, label = 'package') {
200
+ const identifier = '[0-9A-Za-z-]+';
201
+ const match = new RegExp(
202
+ `^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)` +
203
+ `(?:-(${identifier}(?:\\.${identifier})*))?` +
204
+ `(?:\\+${identifier}(?:\\.${identifier})*)?$`
205
+ ).exec(version);
206
+ const prerelease = match?.[4] ?? '';
207
+ const hasInvalidNumericIdentifier = prerelease
208
+ .split('.')
209
+ .some((part) => /^\d+$/.test(part) && part.length > 1 && part.startsWith('0'));
210
+ if (!match || hasInvalidNumericIdentifier) {
211
+ throw new InstallationError(
212
+ `${label} has an invalid semantic version: ${JSON.stringify(version)}`
213
+ );
214
+ }
215
+ return {
216
+ numbers: [BigInt(match[1]), BigInt(match[2]), BigInt(match[3])],
217
+ prerelease
218
+ };
17
219
  }
18
220
 
19
221
  try {
222
+ // Resolve and validate the implementation even for --version. Reporting the
223
+ // wrapper version alone used to hide partially-updated installations where
224
+ // this package was current but its nested CLI (and deploy stack) was stale.
225
+ const cli = resolveCli();
226
+
20
227
  if (process.argv[2] === '-v' || process.argv[2] === '--version') {
21
- process.stdout.write(`${readPackageVersion()}\n`);
228
+ process.stdout.write(`${cli.version}\n`);
22
229
  process.exit(0);
23
230
  }
24
231
 
25
- const { main } = await import('@agentworkforce/cli/dist/cli.js');
232
+ // Import the entry from the exact package whose version was checked above;
233
+ // do not ask the module resolver a second time and risk selecting a different
234
+ // hoisted or nested copy.
235
+ const { main } = await import(cli.entryUrl);
26
236
  await main();
27
237
  } catch (err) {
28
- process.stderr.write(`${err?.stack ?? String(err)}\n`);
238
+ process.stderr.write(
239
+ `${err instanceof InstallationError ? err.message : (err?.stack ?? String(err))}\n`
240
+ );
29
241
  process.exit(1);
30
242
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentworkforce",
3
- "version": "4.1.27",
3
+ "version": "4.1.30",
4
4
  "private": false,
5
5
  "description": "Top-level installer for the Agent Workforce CLI (installs the `agentworkforce` command). Wraps @agentworkforce/cli.",
6
6
  "type": "module",
@@ -17,7 +17,7 @@
17
17
  "node": ">=22"
18
18
  },
19
19
  "dependencies": {
20
- "@agentworkforce/cli": "4.1.27"
20
+ "@agentworkforce/cli": "4.1.30"
21
21
  },
22
22
  "repository": {
23
23
  "type": "git",