harness-everything 0.20.5 → 0.20.7
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +13 -0
- package/ci/mechanism-2ao-plugin-sync.test.js +82 -0
- package/docs/platform-capabilities.md +1 -1
- package/docs/troubleshooting.md +1 -1
- package/opencode-plugin/plugin.json +1 -1
- package/package.json +1 -1
- package/plugins/harness-everything/.codex-plugin/plugin.json +1 -1
- package/plugins/harness-everything/plugin.json +1 -1
- package/sbom.cdx.json +7 -7
- package/scripts/plugin-sync.js +81 -4
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"name": "harness-everything",
|
|
11
11
|
"source": "./",
|
|
12
12
|
"description": "Harness OS: an orchestrated agent operating system — tier routing, circuit breaker guardrails, verification gates, WAL session memory, and self-evolving workspace rules.",
|
|
13
|
-
"version": "0.20.
|
|
13
|
+
"version": "0.20.7",
|
|
14
14
|
"author": {
|
|
15
15
|
"name": "Miya Daniel"
|
|
16
16
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "harness-everything",
|
|
3
3
|
"description": "Harness OS: an orchestrated agent operating system — tier routing, circuit breaker guardrails, verification gates, WAL session memory, and self-evolving workspace rules.",
|
|
4
|
-
"version": "0.20.
|
|
4
|
+
"version": "0.20.7",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Miya Daniel"
|
|
7
7
|
},
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project are documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.20.7](https://github.com/dyphn1/Harness-everything/compare/v0.20.6...v0.20.7) (2026-09-22)
|
|
6
|
+
|
|
7
|
+
### Bug Fixes
|
|
8
|
+
|
|
9
|
+
* **distribution:** emit per-host progress so plugin-sync does not look hung ([d746871](https://github.com/dyphn1/Harness-everything/commit/d746871b7a39c10decc841e9982b27982271df1e)), closes [#238](https://github.com/dyphn1/Harness-everything/issues/238)
|
|
10
|
+
|
|
11
|
+
## [0.20.6](https://github.com/dyphn1/Harness-everything/compare/v0.20.5...v0.20.6) (2026-09-22)
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* **distribution:** match resolve-on-path test extension casing to fixture files ([07f0e5a](https://github.com/dyphn1/Harness-everything/commit/07f0e5a815e779f5596d5c79d448b9ab383ee361))
|
|
16
|
+
* **distribution:** resolve codex/claude on windows in path order, not exe preference ([048c5e4](https://github.com/dyphn1/Harness-everything/commit/048c5e40e0d4727700234c49e509fc6edfac4f01)), closes [#236](https://github.com/dyphn1/Harness-everything/issues/236)
|
|
17
|
+
|
|
5
18
|
## [0.20.5](https://github.com/dyphn1/Harness-everything/compare/v0.20.4...v0.20.5) (2026-09-22)
|
|
6
19
|
|
|
7
20
|
### Bug Fixes
|
|
@@ -9,6 +9,7 @@ const {
|
|
|
9
9
|
findMarketplace,
|
|
10
10
|
findPlugin,
|
|
11
11
|
parseJsonOutput,
|
|
12
|
+
resolveOnPath,
|
|
12
13
|
sync
|
|
13
14
|
} = require('../scripts/plugin-sync');
|
|
14
15
|
|
|
@@ -237,6 +238,60 @@ assert.strictEqual(findPlugin({ plugins: [{ id: 'harness-everything@harness-ever
|
|
|
237
238
|
assert.ok(result.results.every(entry => entry.reasonCode === 'cli-not-found'), 'an absent CLI must stay distinguishable from a capability boundary');
|
|
238
239
|
}
|
|
239
240
|
|
|
241
|
+
{
|
|
242
|
+
// Each host sync is fully synchronous and does real network I/O; without a
|
|
243
|
+
// progress notice emitted before that work starts, the terminal stays
|
|
244
|
+
// silent for the whole run, which is indistinguishable from a hang.
|
|
245
|
+
const events = [];
|
|
246
|
+
const fake = fakeHost({ claudeInstalled: true, codexInstalled: true });
|
|
247
|
+
const runner = (command, args, opts) => {
|
|
248
|
+
events.push(`call:${command}`);
|
|
249
|
+
return fake.runner(command, args, opts);
|
|
250
|
+
};
|
|
251
|
+
const result = sync({ host: 'all', repository: 'dyphn1/Harness-everything', ref: 'main' }, {
|
|
252
|
+
runner,
|
|
253
|
+
commandAvailable: () => true,
|
|
254
|
+
log: () => {},
|
|
255
|
+
warn: message => events.push(`warn:${message}`)
|
|
256
|
+
});
|
|
257
|
+
assert.strictEqual(result.failed, 0);
|
|
258
|
+
assert.ok(events.includes('warn:[...] claude: syncing'), 'a claude progress notice must be emitted');
|
|
259
|
+
assert.ok(events.includes('warn:[...] codex: syncing'), 'a codex progress notice must be emitted');
|
|
260
|
+
assert.ok(events.indexOf('warn:[...] claude: syncing') < events.indexOf('call:claude'), 'the claude notice must be emitted before its first host command, not buffered until the run finishes');
|
|
261
|
+
assert.ok(events.indexOf('warn:[...] codex: syncing') < events.indexOf('call:codex'), 'the codex notice must be emitted before its first host command');
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// resolveOnPath() is pure filesystem logic (platform choice happens only at
|
|
265
|
+
// the defaultRunner call site), so its directory-order-first contract runs
|
|
266
|
+
// on every CI OS, not only Windows.
|
|
267
|
+
{
|
|
268
|
+
const base = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-plugin-sync-resolve-'));
|
|
269
|
+
const cmdDir = path.join(base, 'cmd-dir');
|
|
270
|
+
const exeDir = path.join(base, 'exe-dir');
|
|
271
|
+
fs.mkdirSync(cmdDir);
|
|
272
|
+
fs.mkdirSync(exeDir);
|
|
273
|
+
fs.writeFileSync(path.join(cmdDir, 'harness-fixture.cmd'), '');
|
|
274
|
+
fs.writeFileSync(path.join(exeDir, 'harness-fixture.exe'), '');
|
|
275
|
+
try {
|
|
276
|
+
// Real PATHEXT is upper-case, but NTFS matches it case-insensitively
|
|
277
|
+
// against lower-case fixture files; this test also runs on Linux/macOS
|
|
278
|
+
// CI, whose case-sensitive filesystems would not, so the fixture extension
|
|
279
|
+
// casing matches the file names exactly. The point under test is
|
|
280
|
+
// directory order versus extension order, not PATHEXT's own casing.
|
|
281
|
+
const pathExt = '.com;.exe;.bat;.cmd';
|
|
282
|
+
const cmdFirst = resolveOnPath('harness-fixture', { pathValue: [cmdDir, exeDir].join(path.delimiter), pathExt });
|
|
283
|
+
assert.strictEqual(String(cmdFirst).toLowerCase(), path.join(cmdDir, 'harness-fixture.cmd').toLowerCase(), 'an earlier PATH directory must win even when a later one has the extension PATHEXT prefers');
|
|
284
|
+
|
|
285
|
+
const exeFirst = resolveOnPath('harness-fixture', { pathValue: [exeDir, cmdDir].join(path.delimiter), pathExt });
|
|
286
|
+
assert.strictEqual(String(exeFirst).toLowerCase(), path.join(exeDir, 'harness-fixture.exe').toLowerCase(), 'PATH order, not extension preference, must decide the match');
|
|
287
|
+
|
|
288
|
+
const missing = resolveOnPath('harness-fixture-does-not-exist', { pathValue: [cmdDir, exeDir].join(path.delimiter), pathExt });
|
|
289
|
+
assert.strictEqual(missing, null, 'an unresolved command must return null, not throw');
|
|
290
|
+
} finally {
|
|
291
|
+
fs.rmSync(base, { recursive: true, force: true });
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
240
295
|
if (process.platform === 'win32') {
|
|
241
296
|
const fixtureDir = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-plugin-sync-command-'));
|
|
242
297
|
const originalPath = process.env.PATH;
|
|
@@ -252,6 +307,33 @@ if (process.platform === 'win32') {
|
|
|
252
307
|
}
|
|
253
308
|
}
|
|
254
309
|
|
|
310
|
+
// End-to-end: when a .cmd shim sits earlier on PATH than an unrelated real
|
|
311
|
+
// .exe, the runner must actually execute the .cmd (not silently prefer
|
|
312
|
+
// whichever candidate Node's non-shell spawn can launch directly).
|
|
313
|
+
if (process.platform === 'win32') {
|
|
314
|
+
const fixtureDir = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-plugin-sync-order-'));
|
|
315
|
+
const shadowDir = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-plugin-sync-shadow-'));
|
|
316
|
+
const originalPath = process.env.PATH;
|
|
317
|
+
try {
|
|
318
|
+
fs.writeFileSync(path.join(fixtureDir, 'harness-order-fixture.cmd'), '@echo off\r\necho current-cmd-shim\r\n');
|
|
319
|
+
// A real, directly-launchable PE binary standing in for an unrelated,
|
|
320
|
+
// stale standalone install later on PATH. cmd.exe itself is a stable,
|
|
321
|
+
// always-present .exe to copy for this purpose.
|
|
322
|
+
fs.copyFileSync(path.join(process.env.SystemRoot || 'C:\\Windows', 'System32', 'cmd.exe'), path.join(shadowDir, 'harness-order-fixture.exe'));
|
|
323
|
+
process.env.PATH = [fixtureDir, shadowDir, originalPath || ''].join(path.delimiter);
|
|
324
|
+
|
|
325
|
+
const context = createContext();
|
|
326
|
+
const result = context.runner('harness-order-fixture', []);
|
|
327
|
+
assert.strictEqual(result.status, 0, 'the earlier PATH entry must run successfully');
|
|
328
|
+
assert.match(result.stdout, /current-cmd-shim/, 'an earlier .cmd must be executed even though a later, directly-launchable .exe also matches');
|
|
329
|
+
} finally {
|
|
330
|
+
if (originalPath === undefined) delete process.env.PATH;
|
|
331
|
+
else process.env.PATH = originalPath;
|
|
332
|
+
fs.rmSync(fixtureDir, { recursive: true, force: true });
|
|
333
|
+
fs.rmSync(shadowDir, { recursive: true, force: true });
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
255
337
|
for (const wrapper of ['scripts/plugin-sync.sh', 'scripts/plugin-sync.ps1']) {
|
|
256
338
|
const text = fs.readFileSync(path.join(ROOT, wrapper), 'utf8');
|
|
257
339
|
assert.match(text, /plugin-sync\.js/);
|
|
@@ -87,7 +87,7 @@ Mutating commands are issued with the current option set and retried without any
|
|
|
87
87
|
|
|
88
88
|
The installed branch is intentionally an update branch even when the current release is already the newest version; the host command may report “already latest.” Codex marketplace upgrade is the current native refresh operation; the command does not edit private cache paths or `config.toml`. These are deterministic command/state tests, not live-host evidence, so `liveHostVerification` remains `Unknown` in the matrix.
|
|
89
89
|
|
|
90
|
-
On Windows, the synchronizer
|
|
90
|
+
On Windows, the synchronizer resolves `codex`/`claude` the same way an interactive shell does: it walks `PATH` directories in order and tries `PATHEXT` extensions within each directory before moving on, so directory order — not which candidate happens to be a directly launchable `.exe` — decides the match. This matters because Node's non-shell `spawnSync` cannot execute a `.cmd`/`.bat` shim directly and, left to its own resolution, can silently prefer an unrelated, older standalone `.exe` elsewhere on `PATH` over a newer npm-installed shim with no error to signal it. Only the one case Windows genuinely requires an interpreter for — a resolved `.cmd`/`.bat` target — is run through `ComSpec`, using the already-resolved absolute path rather than re-resolving the bare command name.
|
|
91
91
|
|
|
92
92
|
### Explicit Codex user-hook compatibility fallback
|
|
93
93
|
|
package/docs/troubleshooting.md
CHANGED
|
@@ -202,7 +202,7 @@ Host CLIs differ in which options and subcommands they implement, so the synchro
|
|
|
202
202
|
- An option the host rejects — `unknown option '--json'` (commander) or `unexpected argument '--json' found` (clap/Codex) — is dropped and the command is retried without it.
|
|
203
203
|
- A Codex build that ships only `codex plugin marketplace` has no `codex plugin list`/`codex plugin add`. The marketplace source is still registered and the host is reported as `[skip] codex: …` with an actionable reason, not as a failure. Upgrade the Codex CLI, or enable the plugin from Codex, to complete installation.
|
|
204
204
|
|
|
205
|
-
If several `codex` binaries are on `PATH` (for example an npm shim plus
|
|
205
|
+
If several `codex`/`claude` binaries are on `PATH` (for example an npm shim plus a separately installed build), the synchronizer resolves the same one your interactive shell would: it walks `PATH` directories in order and, on Windows, tries `PATHEXT` extensions within each directory before moving to the next — matching `cmd.exe`/PowerShell/`which` resolution, so directory order decides the match, not which candidate happens to be a directly launchable `.exe`. If the reported capabilities still look stale, an orphaned install is genuinely earlier on `PATH` than the one you expect; check `where codex` (Windows) or `which -a codex` (POSIX) to see the full resolution order, and remove or reorder the stale entry.
|
|
206
206
|
|
|
207
207
|
See [openai-plugin.md](openai-plugin.md) for installation and validation details.
|
|
208
208
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
|
|
3
3
|
"name": "harness-everything",
|
|
4
|
-
"version": "0.20.
|
|
4
|
+
"version": "0.20.7",
|
|
5
5
|
"description": "Invariant-first engineering harness for routing, verification, and failure recovery while preserving agent autonomy.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Miya Daniel",
|
package/sbom.cdx.json
CHANGED
|
@@ -2,23 +2,23 @@
|
|
|
2
2
|
"$schema": "https://cyclonedx.org/schema/bom-1.7.schema.json",
|
|
3
3
|
"bomFormat": "CycloneDX",
|
|
4
4
|
"specVersion": "1.7",
|
|
5
|
-
"serialNumber": "urn:uuid:
|
|
5
|
+
"serialNumber": "urn:uuid:12ceeeac-7f99-5fb2-82a3-768d5d708e00",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-09-
|
|
8
|
+
"timestamp": "2026-09-22T16:17:00.105Z",
|
|
9
9
|
"tools": [
|
|
10
10
|
{
|
|
11
11
|
"vendor": "Harness Everything",
|
|
12
12
|
"name": "generate-sbom.js",
|
|
13
|
-
"version": "0.20.
|
|
13
|
+
"version": "0.20.7"
|
|
14
14
|
}
|
|
15
15
|
],
|
|
16
16
|
"component": {
|
|
17
17
|
"type": "application",
|
|
18
|
-
"bom-ref": "pkg:npm/harness-everything@0.20.
|
|
18
|
+
"bom-ref": "pkg:npm/harness-everything@0.20.7",
|
|
19
19
|
"name": "harness-everything",
|
|
20
|
-
"version": "0.20.
|
|
21
|
-
"purl": "pkg:npm/harness-everything@0.20.
|
|
20
|
+
"version": "0.20.7",
|
|
21
|
+
"purl": "pkg:npm/harness-everything@0.20.7"
|
|
22
22
|
}
|
|
23
23
|
},
|
|
24
24
|
"components": [
|
|
@@ -1999,7 +1999,7 @@
|
|
|
1999
1999
|
]
|
|
2000
2000
|
},
|
|
2001
2001
|
{
|
|
2002
|
-
"ref": "pkg:npm/harness-everything@0.20.
|
|
2002
|
+
"ref": "pkg:npm/harness-everything@0.20.7",
|
|
2003
2003
|
"dependsOn": [
|
|
2004
2004
|
"pkg:npm/%40commitlint/cli@19.8.1",
|
|
2005
2005
|
"pkg:npm/%40commitlint/config-conventional@19.8.1",
|
package/scripts/plugin-sync.js
CHANGED
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
const { spawnSync } = require('child_process');
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const path = require('path');
|
|
14
16
|
|
|
15
17
|
const DEFAULTS = Object.freeze({
|
|
16
18
|
marketplace: 'harness-everything',
|
|
@@ -19,17 +21,83 @@ const DEFAULTS = Object.freeze({
|
|
|
19
21
|
ref: process.env.HARNESS_PLUGIN_REF || 'main'
|
|
20
22
|
});
|
|
21
23
|
|
|
24
|
+
// Windows CLIs that ship as a .cmd/.bat shim are not executable via the same
|
|
25
|
+
// direct CreateProcess call Node uses for a .exe, and — unlike a real shell —
|
|
26
|
+
// Node's non-shell spawnSync does not search PATHEXT-suffixed candidates for
|
|
27
|
+
// a bare command name at all. When more than one `codex`/`claude` is on
|
|
28
|
+
// PATH (an npm shim plus a separately installed .exe is a common pairing),
|
|
29
|
+
// the non-shell spawn silently resolves whichever entry happens to be a real
|
|
30
|
+
// .exe, regardless of PATH order — so an unrelated, older standalone install
|
|
31
|
+
// can shadow a newer npm-updated shim with no error to signal it.
|
|
32
|
+
//
|
|
33
|
+
// resolveOnPath() replicates what a real shell / `where.exe` does instead:
|
|
34
|
+
// walk PATH directories in order, and within each directory try PATHEXT
|
|
35
|
+
// extensions in order, returning the first match. Directory order wins over
|
|
36
|
+
// extension preference, exactly like cmd.exe and PowerShell's own lookup, so
|
|
37
|
+
// the result matches what typing the command in a terminal would run.
|
|
38
|
+
function resolveOnPath(command, { pathValue = process.env.PATH || process.env.Path || '', pathExt = process.env.PATHEXT || '.COM;.EXE;.BAT;.CMD' } = {}) {
|
|
39
|
+
if (path.isAbsolute(command)) return fs.existsSync(command) ? command : null;
|
|
40
|
+
|
|
41
|
+
const dirs = pathValue.split(path.delimiter).filter(Boolean);
|
|
42
|
+
const extensions = pathExt.split(';').map(ext => ext.trim()).filter(Boolean);
|
|
43
|
+
const hasKnownExtension = extensions.some(ext => command.toLowerCase().endsWith(ext.toLowerCase()));
|
|
44
|
+
|
|
45
|
+
for (const dir of dirs) {
|
|
46
|
+
if (hasKnownExtension) {
|
|
47
|
+
const candidate = path.join(dir, command);
|
|
48
|
+
if (fs.existsSync(candidate)) return candidate;
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
for (const ext of extensions) {
|
|
52
|
+
const candidate = path.join(dir, command + ext);
|
|
53
|
+
if (fs.existsSync(candidate)) return candidate;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// A .bat/.cmd target is not a Win32 executable image; Windows requires the
|
|
60
|
+
// cmd.exe interpreter to run one at all, the same way a shebang-less script
|
|
61
|
+
// needs an explicit interpreter on POSIX. This is the one case a shell is
|
|
62
|
+
// unavoidable, and only that resolved file is passed to it — never the bare
|
|
63
|
+
// command name or user-controlled arguments re-interpreted as a command line.
|
|
64
|
+
const SHELL_REQUIRED_EXTENSIONS = new Set(['.bat', '.cmd']);
|
|
65
|
+
|
|
66
|
+
function runResolved(resolved, args, options) {
|
|
67
|
+
const ext = path.extname(resolved).toLowerCase();
|
|
68
|
+
if (SHELL_REQUIRED_EXTENSIONS.has(ext)) {
|
|
69
|
+
const comspec = process.env.ComSpec || process.env.COMSPEC || 'cmd.exe';
|
|
70
|
+
return spawnSync(comspec, ['/d', '/c', resolved, ...args], options);
|
|
71
|
+
}
|
|
72
|
+
return spawnSync(resolved, args, options);
|
|
73
|
+
}
|
|
74
|
+
|
|
22
75
|
function defaultRunner(command, args) {
|
|
23
76
|
const options = {
|
|
24
77
|
encoding: 'utf8',
|
|
25
78
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
26
79
|
windowsHide: true
|
|
27
80
|
};
|
|
81
|
+
|
|
82
|
+
if (process.platform === 'win32') {
|
|
83
|
+
const resolved = resolveOnPath(command);
|
|
84
|
+
if (resolved) {
|
|
85
|
+
const result = runResolved(resolved, args, options);
|
|
86
|
+
return {
|
|
87
|
+
status: result.status === null ? 1 : result.status,
|
|
88
|
+
stdout: result.stdout || '',
|
|
89
|
+
stderr: result.stderr || '',
|
|
90
|
+
error: result.error || null
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
28
95
|
let result = spawnSync(command, args, options);
|
|
29
96
|
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
//
|
|
97
|
+
// Resolution above only checks the filesystem; if PATH itself could not be
|
|
98
|
+
// read, or the command is reachable through a mechanism resolveOnPath does
|
|
99
|
+
// not model (e.g. an App Execution Alias), fall back to letting the shell
|
|
100
|
+
// do its own resolution rather than failing outright.
|
|
33
101
|
if (process.platform === 'win32' && result.error && ['EACCES', 'EINVAL', 'ENOENT'].includes(result.error.code)) {
|
|
34
102
|
const comspec = process.env.ComSpec || process.env.COMSPEC || 'cmd.exe';
|
|
35
103
|
result = spawnSync(comspec, ['/d', '/c', command, ...args], options);
|
|
@@ -507,7 +575,15 @@ Options:
|
|
|
507
575
|
function sync(options, dependencies = {}) {
|
|
508
576
|
const context = createContext({ ...options, ...dependencies });
|
|
509
577
|
const hosts = context.host === 'all' ? ['claude', 'codex'] : [context.host];
|
|
510
|
-
|
|
578
|
+
// Each host sync is fully synchronous and the marketplace update/refresh
|
|
579
|
+
// step does real network I/O (observed ~10s+ for Claude alone), so without
|
|
580
|
+
// this the terminal stays completely silent until every host finishes —
|
|
581
|
+
// indistinguishable from a hang. This goes to stderr (context.warn), not
|
|
582
|
+
// stdout, so `--json` consumers' stdout stays pure JSON.
|
|
583
|
+
const results = hosts.map(host => {
|
|
584
|
+
context.warn(`[...] ${host}: syncing`);
|
|
585
|
+
return host === 'claude' ? syncClaude(context) : syncCodex(context);
|
|
586
|
+
});
|
|
511
587
|
const summary = {
|
|
512
588
|
repository: context.repository,
|
|
513
589
|
marketplace: context.marketplace,
|
|
@@ -563,6 +639,7 @@ module.exports = {
|
|
|
563
639
|
main,
|
|
564
640
|
parseArgs,
|
|
565
641
|
parseJsonOutput,
|
|
642
|
+
resolveOnPath,
|
|
566
643
|
sync,
|
|
567
644
|
textHasMarketplace,
|
|
568
645
|
textHasPlugin
|