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 +9 -0
- package/README.md +11 -0
- package/bin/agentworkforce.js +222 -10
- package/package.json +2 -2
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.
|
package/bin/agentworkforce.js
CHANGED
|
@@ -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
|
|
10
|
-
|
|
11
|
-
|
|
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
|
|
24
|
+
throw new InstallationError(`Could not read ${label} package version.`);
|
|
15
25
|
}
|
|
16
|
-
return pkg
|
|
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(`${
|
|
228
|
+
process.stdout.write(`${cli.version}\n`);
|
|
22
229
|
process.exit(0);
|
|
23
230
|
}
|
|
24
231
|
|
|
25
|
-
|
|
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(
|
|
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.
|
|
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.
|
|
20
|
+
"@agentworkforce/cli": "4.1.30"
|
|
21
21
|
},
|
|
22
22
|
"repository": {
|
|
23
23
|
"type": "git",
|