@vltpkg/cli-sdk 1.4.0 → 1.6.0
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/allow-skills.d.ts +36 -0
- package/dist/allow-skills.js +49 -0
- package/dist/commands/build.d.ts +15 -4
- package/dist/commands/build.js +50 -12
- package/dist/commands/ci.d.ts +2 -2
- package/dist/commands/ci.js +10 -2
- package/dist/commands/create.d.ts +2 -1
- package/dist/commands/create.js +2 -3
- package/dist/commands/exec.d.ts +2 -1
- package/dist/commands/exec.js +2 -3
- package/dist/commands/init.js +4 -3
- package/dist/commands/install/reporter.d.ts +1 -1
- package/dist/commands/install/reporter.js +91 -5
- package/dist/commands/install.d.ts +50 -4
- package/dist/commands/install.js +107 -25
- package/dist/commands/list.js +8 -2
- package/dist/commands/publish.d.ts +0 -1
- package/dist/commands/publish.js +27 -16
- package/dist/commands/query.js +8 -2
- package/dist/commands/setup.d.ts +28 -2
- package/dist/commands/setup.js +185 -3
- package/dist/commands/skills.d.ts +56 -0
- package/dist/commands/skills.js +267 -0
- package/dist/commands/token.js +1 -1
- package/dist/commands/uninstall.d.ts +15 -4
- package/dist/commands/uninstall.js +32 -4
- package/dist/commands/unpublish.d.ts +0 -1
- package/dist/commands/unpublish.js +17 -4
- package/dist/commands/update.d.ts +13 -1
- package/dist/commands/update.js +18 -1
- package/dist/config/definition.d.ts +52 -0
- package/dist/config/definition.js +109 -1
- package/dist/config/index.d.ts +26 -3
- package/dist/config/index.js +149 -7
- package/dist/custom-help.js +4 -0
- package/dist/global-bin-dir.d.ts +37 -0
- package/dist/global-bin-dir.js +138 -0
- package/dist/global-project.d.ts +18 -0
- package/dist/global-project.js +67 -0
- package/dist/global-state.d.ts +24 -0
- package/dist/global-state.js +94 -0
- package/dist/global.d.ts +78 -0
- package/dist/global.js +409 -0
- package/dist/index.js +14 -3
- package/dist/install-receipt.d.ts +40 -0
- package/dist/install-receipt.js +69 -0
- package/dist/load-command.d.ts +2 -0
- package/dist/migrate-workspaces.d.ts +16 -0
- package/dist/migrate-workspaces.js +44 -0
- package/dist/output.js +13 -0
- package/dist/parse-add-remove-args.js +2 -14
- package/dist/print-err.js +4 -1
- package/dist/publish-config.d.ts +9 -0
- package/dist/publish-config.js +8 -0
- package/dist/query-auth.d.ts +3 -2
- package/dist/query-auth.js +6 -4
- package/dist/reload-config.js +2 -1
- package/dist/require-registry.d.ts +7 -0
- package/dist/require-registry.js +13 -1
- package/dist/self-update.d.ts +74 -0
- package/dist/self-update.js +401 -0
- package/dist/shell-setup/env.d.ts +9 -0
- package/dist/shell-setup/env.js +25 -0
- package/dist/shell-setup/index.d.ts +69 -0
- package/dist/shell-setup/index.js +213 -0
- package/dist/shell-setup/posix.d.ts +36 -0
- package/dist/shell-setup/posix.js +158 -0
- package/dist/shell-setup/windows.d.ts +39 -0
- package/dist/shell-setup/windows.js +151 -0
- package/dist/shell.d.ts +18 -0
- package/dist/shell.js +110 -0
- package/dist/skills-prompt.d.ts +28 -0
- package/dist/skills-prompt.js +78 -0
- package/package.json +30 -28
|
@@ -46,6 +46,7 @@ const canonicalCommands = {
|
|
|
46
46
|
'run-exec': 'run-exec',
|
|
47
47
|
run: 'run',
|
|
48
48
|
setup: 'setup',
|
|
49
|
+
skills: 'skills',
|
|
49
50
|
token: 'token',
|
|
50
51
|
uninstall: 'uninstall',
|
|
51
52
|
unpublish: 'unpublish',
|
|
@@ -473,6 +474,66 @@ export const definition = j
|
|
|
473
474
|
description: `Specify named workspace group names to load and operate on
|
|
474
475
|
when doing recursive operations on workspaces.`,
|
|
475
476
|
},
|
|
477
|
+
})
|
|
478
|
+
.flag({
|
|
479
|
+
global: {
|
|
480
|
+
short: 'g',
|
|
481
|
+
description: `Operate on the global project instead of the current
|
|
482
|
+
one. Supported by \`install\`, \`uninstall\`, \`list\`,
|
|
483
|
+
\`query\` and \`build\`.
|
|
484
|
+
|
|
485
|
+
Each global package is installed in its own workspace of
|
|
486
|
+
the project in \`--global-dir\`, and its bins are linked
|
|
487
|
+
into \`--global-bin-dir\`.`,
|
|
488
|
+
},
|
|
489
|
+
})
|
|
490
|
+
.opt({
|
|
491
|
+
'global-dir': {
|
|
492
|
+
hint: 'path',
|
|
493
|
+
default: xdg.data('global'),
|
|
494
|
+
description: `Location of the global project used by \`--global\`.
|
|
495
|
+
Defaults to \`vlt/global\` in the XDG data directory.
|
|
496
|
+
|
|
497
|
+
Only read from the command line, environment or the user
|
|
498
|
+
config, never from a project \`vlt.json\`. A leading
|
|
499
|
+
\`~\` is the home directory. Must be absolute in the
|
|
500
|
+
user config.`,
|
|
501
|
+
},
|
|
502
|
+
})
|
|
503
|
+
.opt({
|
|
504
|
+
'global-bin-dir': {
|
|
505
|
+
hint: 'path',
|
|
506
|
+
description: `Folder where bins of global packages are linked.
|
|
507
|
+
|
|
508
|
+
Defaults to the folder of the \`vlt\` found in your
|
|
509
|
+
\`PATH\` when it is writable, so bins run right away.
|
|
510
|
+
If it is not writable, \`~/.local/bin\` is used when
|
|
511
|
+
it is in your \`PATH\` (not on Windows). Otherwise
|
|
512
|
+
\`<global-dir>/bin\`.
|
|
513
|
+
|
|
514
|
+
Only read from the command line, environment or the
|
|
515
|
+
user config, never from a project \`vlt.json\`. A
|
|
516
|
+
leading \`~\` is the home directory. Must be absolute
|
|
517
|
+
in the user config.`,
|
|
518
|
+
},
|
|
519
|
+
})
|
|
520
|
+
.flag({
|
|
521
|
+
path: {
|
|
522
|
+
description: `With \`vlt setup\`, add the global bin folder
|
|
523
|
+
(\`--global-bin-dir\`) to your \`PATH\` by editing
|
|
524
|
+
your shell config (your user \`Path\` on Windows).
|
|
525
|
+
Asks first, unless \`--yes\`. Command line only.`,
|
|
526
|
+
},
|
|
527
|
+
'no-modify-path': {
|
|
528
|
+
description: `Never edit shell config files (or the user
|
|
529
|
+
\`Path\` on Windows) to add a folder to your
|
|
530
|
+
\`PATH\`: turns off \`vlt setup --path\` edits
|
|
531
|
+
and the \`vlt install -g\` offer (printing the line
|
|
532
|
+
to add instead), and the \`vlt setup\` PATH prompt.
|
|
533
|
+
Set \`VLT_NO_MODIFY_PATH=1\` (any value but \`0\`)
|
|
534
|
+
in images or on machines whose dotfiles are
|
|
535
|
+
managed elsewhere.`,
|
|
536
|
+
},
|
|
476
537
|
})
|
|
477
538
|
.opt({
|
|
478
539
|
scope: {
|
|
@@ -525,7 +586,9 @@ export const definition = j
|
|
|
525
586
|
config: {
|
|
526
587
|
hint: 'all | user | project',
|
|
527
588
|
description: `Specify which configuration to show or operate on when running
|
|
528
|
-
\`vlt config\` commands
|
|
589
|
+
\`vlt config\` commands, and which file
|
|
590
|
+
\`vlt skills link\` saves \`allow-skills\` to. For read
|
|
591
|
+
operations (get, pick, list):
|
|
529
592
|
\`all\` shows merged configuration from both user and project
|
|
530
593
|
files (default). For write operations (set, delete, edit):
|
|
531
594
|
defaults to \`project\`. \`user\` shows/modifies only user-level
|
|
@@ -729,6 +792,17 @@ export const definition = j
|
|
|
729
792
|
\`add\` or \`update\`. Values a config file already
|
|
730
793
|
has are left alone.`,
|
|
731
794
|
},
|
|
795
|
+
'migrate-workspaces': {
|
|
796
|
+
description: `Save workspaces read from the \`workspaces\` field of
|
|
797
|
+
the root \`package.json\` or from
|
|
798
|
+
\`pnpm-workspace.yaml\` to the project \`vlt.json\` on
|
|
799
|
+
\`vlt install\`. \`vlt.json\` then takes precedence.
|
|
800
|
+
|
|
801
|
+
Set \`--no-migrate-workspaces\` to skip this; an
|
|
802
|
+
already saved \`workspaces\` field stays until
|
|
803
|
+
removed.`,
|
|
804
|
+
default: true,
|
|
805
|
+
},
|
|
732
806
|
})
|
|
733
807
|
.opt({
|
|
734
808
|
'save-prefix': {
|
|
@@ -796,6 +870,40 @@ export const definition = j
|
|
|
796
870
|
Runs scripts only for direct dependencies of the current project and any occurrences
|
|
797
871
|
of a specific dependency with the name "my-package" anywhere in the dependency graph.`,
|
|
798
872
|
},
|
|
873
|
+
})
|
|
874
|
+
.opt({
|
|
875
|
+
'allow-skills': {
|
|
876
|
+
hint: 'query',
|
|
877
|
+
description: `Link the agent skills (folders with a \`SKILL.md\`) shipped by
|
|
878
|
+
installed packages matching this DSS query into
|
|
879
|
+
the project's agent skill dirs (see \`skills-dir\`)
|
|
880
|
+
after \`install\`, \`ci\`, \`update\` or
|
|
881
|
+
\`uninstall\`. End a selector with
|
|
882
|
+
\`:skill(<name>, …)\` to pick single skills (\`*\`
|
|
883
|
+
globs, double-quoted names). Unset (the default) or
|
|
884
|
+
\`:not(*)\` links nothing and skips all skills work.
|
|
885
|
+
|
|
886
|
+
\`vlt install --allow-skills=<query>\` saves the query
|
|
887
|
+
to the project \`vlt.json\`; \`vlt skills link
|
|
888
|
+
[<query>]\` sets, saves and applies it.
|
|
889
|
+
|
|
890
|
+
Example: --allow-skills="#agent-browser:skill(agent-browser)"
|
|
891
|
+
Links the agent-browser skill from a package with the same name.`,
|
|
892
|
+
},
|
|
893
|
+
})
|
|
894
|
+
.optList({
|
|
895
|
+
'skills-dir': {
|
|
896
|
+
hint: 'path',
|
|
897
|
+
description: `Dirs to link agent skills into (see \`allow-skills\`),
|
|
898
|
+
relative to the project root. Replaces detection:
|
|
899
|
+
\`.agents/skills\`, plus \`<folder>/skills\` for each
|
|
900
|
+
agent folder at the project root (e.g. \`.claude/\`),
|
|
901
|
+
\`.claude/skills\` inside Claude Code, and existing
|
|
902
|
+
\`.<name>/skills\` dirs. An empty list (\`[]\` in
|
|
903
|
+
vlt.json, or \`--skills-dir=\`) links nothing. Each
|
|
904
|
+
dir must be inside the project, not in
|
|
905
|
+
node_modules.`,
|
|
906
|
+
},
|
|
799
907
|
})
|
|
800
908
|
.opt({
|
|
801
909
|
access: {
|
package/dist/config/index.d.ts
CHANGED
|
@@ -77,7 +77,7 @@ export type ConfigFileData = ConfigFileDataNoCommand & {
|
|
|
77
77
|
export type ConfigOptionsNoExtras = {
|
|
78
78
|
[k in keyof OptionsResults<ConfigDefinitions>]: k extends (RecordField) ? RecordString : k extends 'command' ? never : OptionsResults<ConfigDefinitions>[k];
|
|
79
79
|
};
|
|
80
|
-
export type ConfigOptions = ConfigOptionsNoExtras & Pick<SpecOptions, 'catalog' | 'catalogs'> & {
|
|
80
|
+
export type ConfigOptions = Omit<ConfigOptionsNoExtras, 'path'> & Pick<SpecOptions, 'catalog' | 'catalogs'> & {
|
|
81
81
|
packageJson: PackageJson;
|
|
82
82
|
scurry: PathScurry;
|
|
83
83
|
projectRoot: string;
|
|
@@ -142,6 +142,24 @@ export declare class Config {
|
|
|
142
142
|
* `~/projects/xyz`, then the highest dir it will check is `~/projects`
|
|
143
143
|
*/
|
|
144
144
|
projectRoot: string;
|
|
145
|
+
/**
|
|
146
|
+
* Global project root, set when `--global` switched to it.
|
|
147
|
+
*/
|
|
148
|
+
globalRoot?: string;
|
|
149
|
+
/**
|
|
150
|
+
* `--global-bin-dir` from cli, env or user config, set with
|
|
151
|
+
* `globalRoot`.
|
|
152
|
+
*/
|
|
153
|
+
globalBinDir?: string;
|
|
154
|
+
/**
|
|
155
|
+
* The global project (`--global-dir`, `--global-bin-dir`: cli > env >
|
|
156
|
+
* user config, never a project `vlt.json`): the one switched to by
|
|
157
|
+
* `--global`, or the one `vlt setup` offers PATH setup for.
|
|
158
|
+
*/
|
|
159
|
+
globalProject(): {
|
|
160
|
+
root: string;
|
|
161
|
+
binDir?: string;
|
|
162
|
+
} | undefined;
|
|
145
163
|
/**
|
|
146
164
|
* `Record<alias, canonical name>` to dereference command aliases.
|
|
147
165
|
*/
|
|
@@ -221,8 +239,11 @@ export declare class Config {
|
|
|
221
239
|
/**
|
|
222
240
|
* Find the local config file and load both it and the user-level config in
|
|
223
241
|
* the XDG config home.
|
|
242
|
+
*
|
|
243
|
+
* When `args` select a `--global` command, the global project is used
|
|
244
|
+
* instead of the current one.
|
|
224
245
|
*/
|
|
225
|
-
loadConfigFile(): Promise<this>;
|
|
246
|
+
loadConfigFile(args?: string[]): Promise<this>;
|
|
226
247
|
/**
|
|
227
248
|
* Force a complete reload of config files from disk.
|
|
228
249
|
* This clears all caches and re-reads config files.
|
|
@@ -238,7 +259,9 @@ export declare class Config {
|
|
|
238
259
|
* only used in tests, resets the memoization
|
|
239
260
|
* @internal
|
|
240
261
|
*/
|
|
241
|
-
reload?: boolean
|
|
262
|
+
reload?: boolean,
|
|
263
|
+
/** switch to the global project on `--global` */
|
|
264
|
+
allowGlobal?: boolean): Promise<LoadedConfig>;
|
|
242
265
|
}
|
|
243
266
|
export type ParsedConfig = Config & {
|
|
244
267
|
command: NonNullable<Config['command']>;
|
package/dist/config/index.js
CHANGED
|
@@ -28,13 +28,15 @@ import { cloneAvailable } from '@vltpkg/tar/clonefile';
|
|
|
28
28
|
import { resetCaches } from '@vltpkg/dep-id';
|
|
29
29
|
import { getOptions } from '@vltpkg/spec';
|
|
30
30
|
import { assertRecordStringString, assertRecordStringT, isObject, isRecordStringString, } from '@vltpkg/types';
|
|
31
|
-
import { find, load, reload, save } from '@vltpkg/vlt-json';
|
|
32
|
-
import { Monorepo } from '@vltpkg/workspaces';
|
|
31
|
+
import { find, load, reload, save, unload } from '@vltpkg/vlt-json';
|
|
32
|
+
import { Monorepo, workspaceCache } from '@vltpkg/workspaces';
|
|
33
33
|
import { readFile, rm, writeFile } from 'node:fs/promises';
|
|
34
|
-
import {
|
|
34
|
+
import { homedir } from 'node:os';
|
|
35
|
+
import { dirname, isAbsolute, resolve } from 'node:path';
|
|
35
36
|
import { PathScurry } from 'path-scurry';
|
|
36
37
|
import { commands, definition, defaultValues, getCommand, isRecordField, recordFields, storeLinkers, } from "./definition.js";
|
|
37
38
|
import { merge } from "./merge.js";
|
|
39
|
+
import { ensureGlobalProject, globalCommands, } from "../global-project.js";
|
|
38
40
|
import { cloneLayer, mergeLayers } from "./merge-layers.js";
|
|
39
41
|
import { assertSpecAliases } from "./spec-aliases.js";
|
|
40
42
|
export { commands, definition, isRecordField, recordFields, };
|
|
@@ -117,6 +119,8 @@ export const recordsToPairs = (obj) => {
|
|
|
117
119
|
const kRecord = Symbol('parsed key=value record');
|
|
118
120
|
// jackspeak's env var name for a field
|
|
119
121
|
const envKey = (k) => `VLT_${k.replace(/[^a-zA-Z0-9]+/g, '_').toUpperCase()}`;
|
|
122
|
+
// `~` and `~/x` (shells leave `--opt=~/x` alone)
|
|
123
|
+
const expandHome = (p) => p.replace(/^~(?=$|[\\/])/, homedir());
|
|
120
124
|
// `parse()` writes every resolved value to `VLT_*`, so a nested vlt
|
|
121
125
|
// can't tell inherited env from env set for it. this holds the
|
|
122
126
|
// parent's explicit values and the `VLT_*` env it wrote.
|
|
@@ -171,7 +175,7 @@ export class Config {
|
|
|
171
175
|
return this.#options;
|
|
172
176
|
const scurry = new PathScurry(this.projectRoot);
|
|
173
177
|
const packageJson = new PackageJson();
|
|
174
|
-
const asRecords = pairsToRecords(this.parse().values);
|
|
178
|
+
const { path: _, ...asRecords } = pairsToRecords(this.parse().values);
|
|
175
179
|
const extras = {
|
|
176
180
|
projectRoot: this.projectRoot,
|
|
177
181
|
signal: this.#abort.signal,
|
|
@@ -214,6 +218,8 @@ export class Config {
|
|
|
214
218
|
this.projectRoot = projectRoot;
|
|
215
219
|
this.#options = undefined;
|
|
216
220
|
resetCaches();
|
|
221
|
+
// cached manifests belong to the old packageJson
|
|
222
|
+
workspaceCache.clear();
|
|
217
223
|
}
|
|
218
224
|
// memoized options() getter value
|
|
219
225
|
#options;
|
|
@@ -240,6 +246,28 @@ export class Config {
|
|
|
240
246
|
* `~/projects/xyz`, then the highest dir it will check is `~/projects`
|
|
241
247
|
*/
|
|
242
248
|
projectRoot;
|
|
249
|
+
/**
|
|
250
|
+
* Global project root, set when `--global` switched to it.
|
|
251
|
+
*/
|
|
252
|
+
globalRoot;
|
|
253
|
+
/**
|
|
254
|
+
* `--global-bin-dir` from cli, env or user config, set with
|
|
255
|
+
* `globalRoot`.
|
|
256
|
+
*/
|
|
257
|
+
globalBinDir;
|
|
258
|
+
// the global project of the `setup` wizard, not switched to
|
|
259
|
+
#setupGlobal;
|
|
260
|
+
/**
|
|
261
|
+
* The global project (`--global-dir`, `--global-bin-dir`: cli > env >
|
|
262
|
+
* user config, never a project `vlt.json`): the one switched to by
|
|
263
|
+
* `--global`, or the one `vlt setup` offers PATH setup for.
|
|
264
|
+
*/
|
|
265
|
+
globalProject() {
|
|
266
|
+
if (this.globalRoot) {
|
|
267
|
+
return { root: this.globalRoot, binDir: this.globalBinDir };
|
|
268
|
+
}
|
|
269
|
+
return this.#setupGlobal;
|
|
270
|
+
}
|
|
243
271
|
/**
|
|
244
272
|
* `Record<alias, canonical name>` to dereference command aliases.
|
|
245
273
|
*/
|
|
@@ -268,6 +296,12 @@ export class Config {
|
|
|
268
296
|
!storeLinkers.includes(badLinker);
|
|
269
297
|
if (fixLinker)
|
|
270
298
|
process.env.VLT_STORE_LINKER = 'unpack';
|
|
299
|
+
// an opt-out of dotfile edits: any value but 0 (or empty) is set
|
|
300
|
+
const noModifyPath = process.env.VLT_NO_MODIFY_PATH;
|
|
301
|
+
if (noModifyPath !== undefined) {
|
|
302
|
+
process.env.VLT_NO_MODIFY_PATH =
|
|
303
|
+
noModifyPath === '' || noModifyPath === '0' ? '0' : '1';
|
|
304
|
+
}
|
|
271
305
|
const envKeys = Object.keys(defaultValues).filter(k => process.env[envKey(k)] !== undefined);
|
|
272
306
|
const parent = parentEnv();
|
|
273
307
|
// file layer values, before env replaces them
|
|
@@ -333,7 +367,27 @@ export class Config {
|
|
|
333
367
|
if (cmdOrFallback !== 'config') {
|
|
334
368
|
assertSpecAliases(pairsToRecords(p.values));
|
|
335
369
|
}
|
|
370
|
+
// a project vlt.json global-bin-dir must not reach a nested vlt -g
|
|
371
|
+
const binDirKey = envKey('global-bin-dir');
|
|
372
|
+
const keepBinDir = !this.globalRoot && !cliKeys.includes('global-bin-dir');
|
|
373
|
+
const binDirEnv = process.env[binDirKey];
|
|
336
374
|
this.jack.writeEnv(p);
|
|
375
|
+
// scripts and nested vlt act on their own project
|
|
376
|
+
delete process.env[envKey('global')];
|
|
377
|
+
// but use the same global dir, even from another cwd
|
|
378
|
+
if (this.globalRoot) {
|
|
379
|
+
process.env[envKey('global-dir')] = this.globalRoot;
|
|
380
|
+
if (this.globalBinDir)
|
|
381
|
+
process.env[binDirKey] = this.globalBinDir;
|
|
382
|
+
else
|
|
383
|
+
delete process.env[binDirKey];
|
|
384
|
+
}
|
|
385
|
+
else if (keepBinDir) {
|
|
386
|
+
if (binDirEnv === undefined)
|
|
387
|
+
delete process.env[binDirKey];
|
|
388
|
+
else
|
|
389
|
+
process.env[binDirKey] = binDirEnv;
|
|
390
|
+
}
|
|
337
391
|
process.env[kParentEnv] = JSON.stringify({
|
|
338
392
|
explicit,
|
|
339
393
|
env: Object.fromEntries(Object.keys(defaultValues).flatMap(k => {
|
|
@@ -641,13 +695,99 @@ export class Config {
|
|
|
641
695
|
/**
|
|
642
696
|
* Find the local config file and load both it and the user-level config in
|
|
643
697
|
* the XDG config home.
|
|
698
|
+
*
|
|
699
|
+
* When `args` select a `--global` command, the global project is used
|
|
700
|
+
* instead of the current one.
|
|
644
701
|
*/
|
|
645
|
-
async loadConfigFile() {
|
|
702
|
+
async loadConfigFile(args) {
|
|
646
703
|
await this.#maybeLoadConfigFile('user');
|
|
704
|
+
const g = args && this.#globalProject(args);
|
|
705
|
+
if (g?.switch) {
|
|
706
|
+
const { root } = g;
|
|
707
|
+
try {
|
|
708
|
+
ensureGlobalProject(root);
|
|
709
|
+
}
|
|
710
|
+
catch (cause) {
|
|
711
|
+
throw error('Could not create the global project', {
|
|
712
|
+
code: 'ECONFIG',
|
|
713
|
+
found: root,
|
|
714
|
+
cause,
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
this.globalBinDir = g.binDir;
|
|
718
|
+
unload('project');
|
|
719
|
+
this.globalRoot = this.projectRoot = root;
|
|
720
|
+
}
|
|
721
|
+
else if (g) {
|
|
722
|
+
this.#setupGlobal = { root: g.root, binDir: g.binDir };
|
|
723
|
+
}
|
|
647
724
|
this.projectRoot = dirname(find('project', this.projectRoot));
|
|
648
725
|
await this.#maybeLoadConfigFile('project');
|
|
649
726
|
return this;
|
|
650
727
|
}
|
|
728
|
+
// global project if `args` run a global command with --global, or
|
|
729
|
+
// `setup`. cli > env > user config, project config is not loaded yet.
|
|
730
|
+
// `switch`: use it as the project (not for the setup wizard)
|
|
731
|
+
#globalProject(args) {
|
|
732
|
+
const { values, positionals } = this.jack.parseRaw(args);
|
|
733
|
+
const cmd = getCommand(positionals[0]);
|
|
734
|
+
if (values.help || !cmd)
|
|
735
|
+
return;
|
|
736
|
+
// `setup --path` acts on the global project, the wizard offers to
|
|
737
|
+
const setup = cmd === 'setup';
|
|
738
|
+
if (!setup && !globalCommands.has(cmd))
|
|
739
|
+
return;
|
|
740
|
+
if (!setup) {
|
|
741
|
+
const env = process.env[envKey('global')];
|
|
742
|
+
const global = values.global ??
|
|
743
|
+
(env !== undefined ?
|
|
744
|
+
env === '1'
|
|
745
|
+
: this.#defaults().global === true);
|
|
746
|
+
if (!global)
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
749
|
+
return {
|
|
750
|
+
root: this.#globalDir(values['global-dir']),
|
|
751
|
+
binDir: this.#globalBinDir(args),
|
|
752
|
+
switch: !setup || values.path === true,
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
#globalDir(cli) {
|
|
756
|
+
// empty means unset, never the cwd
|
|
757
|
+
const dir = cli || process.env[envKey('global-dir')];
|
|
758
|
+
if (dir)
|
|
759
|
+
return resolve(expandHome(dir));
|
|
760
|
+
const user = expandHome(String(this.#defaults()['global-dir'] || defaultValues['global-dir']));
|
|
761
|
+
// would differ per cwd
|
|
762
|
+
if (!isAbsolute(user)) {
|
|
763
|
+
throw error('global-dir in the user config must be absolute', {
|
|
764
|
+
code: 'ECONFIG',
|
|
765
|
+
found: user,
|
|
766
|
+
});
|
|
767
|
+
}
|
|
768
|
+
// normalize separators (`~/g` on win32)
|
|
769
|
+
return resolve(user);
|
|
770
|
+
}
|
|
771
|
+
// cli > env > user config, like `#globalProject`
|
|
772
|
+
#globalBinDir(args) {
|
|
773
|
+
const { values } = this.jack.parseRaw(args);
|
|
774
|
+
// empty means unset
|
|
775
|
+
const dir = values['global-bin-dir'] ||
|
|
776
|
+
process.env[envKey('global-bin-dir')];
|
|
777
|
+
if (dir)
|
|
778
|
+
return resolve(expandHome(dir));
|
|
779
|
+
const user = this.#defaults()['global-bin-dir'];
|
|
780
|
+
if (typeof user !== 'string' || !user)
|
|
781
|
+
return;
|
|
782
|
+
const p = expandHome(user);
|
|
783
|
+
if (!isAbsolute(p)) {
|
|
784
|
+
throw error('global-bin-dir in the user config must be absolute', {
|
|
785
|
+
code: 'ECONFIG',
|
|
786
|
+
found: p,
|
|
787
|
+
});
|
|
788
|
+
}
|
|
789
|
+
return resolve(p);
|
|
790
|
+
}
|
|
651
791
|
/**
|
|
652
792
|
* Clear cached config values to force re-reading from updated files.
|
|
653
793
|
* @internal
|
|
@@ -694,11 +834,13 @@ export class Config {
|
|
|
694
834
|
* only used in tests, resets the memoization
|
|
695
835
|
* @internal
|
|
696
836
|
*/
|
|
697
|
-
reload = false
|
|
837
|
+
reload = false,
|
|
838
|
+
/** switch to the global project on `--global` */
|
|
839
|
+
allowGlobal = true) {
|
|
698
840
|
if (this.#loaded && !reload)
|
|
699
841
|
return this.#loaded;
|
|
700
842
|
const a = new Config(definition, projectRoot);
|
|
701
|
-
const b = await a.loadConfigFile();
|
|
843
|
+
const b = await a.loadConfigFile(allowGlobal ? argv : undefined);
|
|
702
844
|
this.#loaded = b.parse(argv);
|
|
703
845
|
return this.#loaded;
|
|
704
846
|
}
|
package/dist/custom-help.js
CHANGED
|
@@ -183,6 +183,10 @@ const commandHelp = {
|
|
|
183
183
|
desc: 'Configure your vlt.io account registries',
|
|
184
184
|
defaultOrder: 0,
|
|
185
185
|
},
|
|
186
|
+
skills: {
|
|
187
|
+
args: '[list|link]',
|
|
188
|
+
desc: 'Manage agent skills from installed packages',
|
|
189
|
+
},
|
|
186
190
|
token: {
|
|
187
191
|
args: '[add|rm]',
|
|
188
192
|
desc: 'Manage authentication tokens',
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { LoadedConfig } from './config/index.ts';
|
|
2
|
+
import type { InstallReceipt } from './install-receipt.ts';
|
|
3
|
+
export type GlobalBinDirSource = 'config' | 'self' | 'local-bin' | 'global-dir';
|
|
4
|
+
export type ResolvedGlobalBinDir = {
|
|
5
|
+
/** canonical (native realpath) */
|
|
6
|
+
dir: string;
|
|
7
|
+
source: GlobalBinDirSource;
|
|
8
|
+
inPath: boolean;
|
|
9
|
+
/** self dir passed over because it is not writable */
|
|
10
|
+
notWritable?: string;
|
|
11
|
+
};
|
|
12
|
+
/** the `vlt` on PATH, when it is the running program */
|
|
13
|
+
export type Self = {
|
|
14
|
+
/** what `which` found: `<dir>/vlt`, `vlt.cmd`, `vlt.exe` */
|
|
15
|
+
found: string;
|
|
16
|
+
/** canonical dirname of `found` */
|
|
17
|
+
dir: string;
|
|
18
|
+
/** realpath of what `found` runs */
|
|
19
|
+
target: string;
|
|
20
|
+
/** `found` is `process.execPath`: a single executable */
|
|
21
|
+
standalone: boolean;
|
|
22
|
+
receipt?: InstallReceipt;
|
|
23
|
+
};
|
|
24
|
+
export type ResolveOptions = {
|
|
25
|
+
/** skip the self dir, eg after a write there failed */
|
|
26
|
+
selfUnwritable?: boolean;
|
|
27
|
+
};
|
|
28
|
+
/** `isInPath`, also matching symlinked entries */
|
|
29
|
+
export declare const isInPathRealpath: (dir: string, PATH?: string) => Promise<boolean>;
|
|
30
|
+
/** the `vlt` found in the `PATH`, if it runs this process */
|
|
31
|
+
export declare const findSelf: () => Promise<Self | undefined>;
|
|
32
|
+
/**
|
|
33
|
+
* Where to link global bins: `--global-bin-dir`, else the dir of the
|
|
34
|
+
* running `vlt` when writable, else `~/.local/bin` when in the `PATH`
|
|
35
|
+
* (not win32), else `<global-dir>/bin`.
|
|
36
|
+
*/
|
|
37
|
+
export declare const resolveGlobalBinDir: (conf: Pick<LoadedConfig, "projectRoot" | "globalBinDir">, { selfUnwritable }?: ResolveOptions) => Promise<ResolvedGlobalBinDir>;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { findCmdShimIfExists } from '@vltpkg/cmd-shim';
|
|
2
|
+
import { error } from '@vltpkg/error-cause';
|
|
3
|
+
import { which } from '@vltpkg/which';
|
|
4
|
+
import { constants } from 'node:fs';
|
|
5
|
+
import { access, mkdir, realpath, stat } from 'node:fs/promises';
|
|
6
|
+
import { homedir } from 'node:os';
|
|
7
|
+
import { basename, delimiter, dirname, isAbsolute, resolve, } from 'node:path';
|
|
8
|
+
import { isInPath } from "./global.js";
|
|
9
|
+
import { globalBinDir, samePath } from "./global-project.js";
|
|
10
|
+
import { readInstallReceipt } from "./install-receipt.js";
|
|
11
|
+
// native realpath: also fixes case on case-insensitive fs
|
|
12
|
+
const real = (p) => realpath(p).catch(() => undefined);
|
|
13
|
+
const writable = (dir) => access(dir, constants.W_OK).then(() => true, () => false);
|
|
14
|
+
/** `isInPath`, also matching symlinked entries */
|
|
15
|
+
export const isInPathRealpath = async (dir, PATH = process.env.PATH ?? '') => {
|
|
16
|
+
if (isInPath(dir, PATH))
|
|
17
|
+
return true;
|
|
18
|
+
const r = await real(dir);
|
|
19
|
+
if (!r)
|
|
20
|
+
return false;
|
|
21
|
+
for (const p of PATH.split(delimiter)) {
|
|
22
|
+
const rp = p && (await real(p));
|
|
23
|
+
if (rp && samePath(rp, r))
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
return false;
|
|
27
|
+
};
|
|
28
|
+
// npx, vlx, run scripts put a project's `.bin` first
|
|
29
|
+
const isDotBin = (d) => {
|
|
30
|
+
const name = (p) => process.platform === 'win32' ?
|
|
31
|
+
basename(p).toLowerCase()
|
|
32
|
+
: basename(p);
|
|
33
|
+
return name(d) === '.bin' && name(dirname(d)) === 'node_modules';
|
|
34
|
+
};
|
|
35
|
+
/** the `vlt` found in the `PATH`, if it runs this process */
|
|
36
|
+
export const findSelf = async () => {
|
|
37
|
+
const found = await which('vlt', { nothrow: true }).catch(() => null);
|
|
38
|
+
if (!found || !isAbsolute(found))
|
|
39
|
+
return;
|
|
40
|
+
// win32 looks in the cwd first
|
|
41
|
+
const foundDir = dirname(found);
|
|
42
|
+
if (!isInPath(foundDir) || isDotBin(foundDir))
|
|
43
|
+
return;
|
|
44
|
+
const dir = await real(foundDir);
|
|
45
|
+
if (!dir || isDotBin(dir))
|
|
46
|
+
return;
|
|
47
|
+
const shim = /\.(cmd|ps1)$/i.test(found) ?
|
|
48
|
+
(await findCmdShimIfExists(found))?.[1]
|
|
49
|
+
: found;
|
|
50
|
+
const target = shim && (await real(shim));
|
|
51
|
+
if (!target)
|
|
52
|
+
return;
|
|
53
|
+
const exec = await real(process.execPath);
|
|
54
|
+
const standalone = !!exec && samePath(target, exec);
|
|
55
|
+
if (!standalone) {
|
|
56
|
+
const script = process.argv[1] && (await real(process.argv[1]));
|
|
57
|
+
if (!script || !samePath(target, script))
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const receipt = await readInstallReceipt(found);
|
|
61
|
+
return {
|
|
62
|
+
found,
|
|
63
|
+
dir,
|
|
64
|
+
target,
|
|
65
|
+
standalone,
|
|
66
|
+
...(receipt ? { receipt } : {}),
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
const resolveDir = async (conf, selfUnwritable) => {
|
|
70
|
+
const configured = conf.globalBinDir;
|
|
71
|
+
if (configured) {
|
|
72
|
+
try {
|
|
73
|
+
await mkdir(configured, { recursive: true });
|
|
74
|
+
}
|
|
75
|
+
catch (cause) {
|
|
76
|
+
throw error('Could not create global-bin-dir', {
|
|
77
|
+
code: 'ECONFIG',
|
|
78
|
+
found: configured,
|
|
79
|
+
cause,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
await access(configured, constants.W_OK);
|
|
84
|
+
}
|
|
85
|
+
catch (cause) {
|
|
86
|
+
throw error('global-bin-dir is not writable', {
|
|
87
|
+
code: 'ECONFIG',
|
|
88
|
+
found: configured,
|
|
89
|
+
cause,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
const dir = await realpath(configured);
|
|
93
|
+
return {
|
|
94
|
+
dir,
|
|
95
|
+
source: 'config',
|
|
96
|
+
inPath: await isInPathRealpath(dir),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
const self = await findSelf();
|
|
100
|
+
if (self && !selfUnwritable && (await writable(self.dir))) {
|
|
101
|
+
return { dir: self.dir, source: 'self', inPath: true };
|
|
102
|
+
}
|
|
103
|
+
const notWritable = self?.dir;
|
|
104
|
+
if (notWritable && process.platform !== 'win32') {
|
|
105
|
+
// never created: only in the PATH if it existed at login
|
|
106
|
+
const local = resolve(homedir(), '.local/bin');
|
|
107
|
+
const st = await stat(local).catch(() => undefined);
|
|
108
|
+
if (st?.isDirectory() &&
|
|
109
|
+
(await isInPathRealpath(local)) &&
|
|
110
|
+
(await writable(local))) {
|
|
111
|
+
const dir = await realpath(local);
|
|
112
|
+
// it may be the self dir that refused a write
|
|
113
|
+
if (!samePath(dir, notWritable)) {
|
|
114
|
+
return { dir, source: 'local-bin', inPath: true, notWritable };
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
const g = globalBinDir(conf.projectRoot);
|
|
119
|
+
await mkdir(g, { recursive: true });
|
|
120
|
+
const dir = await realpath(g);
|
|
121
|
+
return {
|
|
122
|
+
dir,
|
|
123
|
+
source: 'global-dir',
|
|
124
|
+
inPath: await isInPathRealpath(dir),
|
|
125
|
+
...(notWritable ? { notWritable } : {}),
|
|
126
|
+
};
|
|
127
|
+
};
|
|
128
|
+
/**
|
|
129
|
+
* Where to link global bins: `--global-bin-dir`, else the dir of the
|
|
130
|
+
* running `vlt` when writable, else `~/.local/bin` when in the `PATH`
|
|
131
|
+
* (not win32), else `<global-dir>/bin`.
|
|
132
|
+
*/
|
|
133
|
+
export const resolveGlobalBinDir = async (conf, { selfUnwritable = false } = {}) => {
|
|
134
|
+
const r = await resolveDir(conf, selfUnwritable);
|
|
135
|
+
// nested vlt agrees
|
|
136
|
+
process.env.VLT_GLOBAL_BIN_DIR = r.dir;
|
|
137
|
+
return r;
|
|
138
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** commands that support `--global` */
|
|
2
|
+
export declare const globalCommands: ReadonlySet<string>;
|
|
3
|
+
/** folder holding one workspace per global package */
|
|
4
|
+
export declare const globalWorkspacesDir = "packages";
|
|
5
|
+
/** fallback dir for bins of global packages */
|
|
6
|
+
export declare const globalBinDir: (root: string) => string;
|
|
7
|
+
/** same path, case-insensitive on win32 */
|
|
8
|
+
export declare const samePath: (a: string, b: string) => boolean;
|
|
9
|
+
/** path is strictly inside dir */
|
|
10
|
+
export declare const isInside: (dir: string, path: string) => boolean;
|
|
11
|
+
/** throws `EUSAGE` unless an unscoped or `@scope/name` pkg name */
|
|
12
|
+
export declare const assertGlobalName: (name: string) => void;
|
|
13
|
+
/** `eslint` -> `eslint-ws` */
|
|
14
|
+
export declare const globalWorkspaceName: (name: string) => string;
|
|
15
|
+
/** `eslint` -> `packages/eslint-ws`, `@s/p` -> `packages/@s+p-ws` */
|
|
16
|
+
export declare const globalWorkspacePath: (name: string) => string;
|
|
17
|
+
/** create the global project skeleton, keeping existing files */
|
|
18
|
+
export declare const ensureGlobalProject: (root: string) => void;
|