pi-extmgr 0.1.26 → 0.1.28
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/README.md +7 -5
- package/package.json +13 -16
- package/src/commands/auto-update.ts +4 -4
- package/src/commands/cache.ts +1 -1
- package/src/commands/history.ts +3 -3
- package/src/commands/install.ts +2 -2
- package/src/commands/registry.ts +7 -7
- package/src/commands/types.ts +1 -1
- package/src/extensions/discovery.ts +4 -3
- package/src/index.ts +15 -15
- package/src/packages/catalog.ts +163 -0
- package/src/packages/discovery.ts +77 -262
- package/src/packages/extensions.ts +10 -5
- package/src/packages/install.ts +42 -37
- package/src/packages/management.ts +145 -99
- package/src/types/index.ts +16 -9
- package/src/ui/async-task.ts +194 -0
- package/src/ui/footer.ts +4 -8
- package/src/ui/help.ts +2 -2
- package/src/ui/package-config.ts +62 -49
- package/src/ui/remote.ts +83 -28
- package/src/ui/theme.ts +2 -2
- package/src/ui/unified.ts +104 -89
- package/src/utils/auto-update.ts +18 -64
- package/src/utils/cache.ts +3 -3
- package/src/utils/command.ts +1 -1
- package/src/utils/format.ts +4 -3
- package/src/utils/history.ts +4 -2
- package/src/utils/mode.ts +1 -1
- package/src/utils/network.ts +10 -2
- package/src/utils/notify.ts +1 -1
- package/src/utils/npm-exec.ts +3 -1
- package/src/utils/package-source.ts +84 -2
- package/src/utils/retry.ts +1 -1
- package/src/utils/settings.ts +17 -8
- package/src/utils/status.ts +16 -12
- package/src/utils/ui-helpers.ts +3 -3
package/README.md
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
7
7
|
|
|
8
8
|
A better way to manage Pi extensions. Browse, install, enable/disable, and remove extensions from one place.
|
|
9
|
+
Built on top of Pi's native package install, update, and config flows, so extmgr stays aligned with current upstream behavior.
|
|
9
10
|
|
|
10
11
|
**🌐 [pi-extmgr landing page](https://ayagmar.github.io/pi-extmgr)**
|
|
11
12
|
|
|
@@ -15,9 +16,9 @@ A better way to manage Pi extensions. Browse, install, enable/disable, and remov
|
|
|
15
16
|
pi install npm:pi-extmgr
|
|
16
17
|
```
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
If Pi is already running, use `/reload`.
|
|
19
20
|
|
|
20
|
-
Requires Node.js `>=22
|
|
21
|
+
Requires Node.js `>=22`.
|
|
21
22
|
|
|
22
23
|
## Features
|
|
23
24
|
|
|
@@ -38,10 +39,11 @@ Requires Node.js `>=22.5.0`.
|
|
|
38
39
|
- npm search/browse with pagination
|
|
39
40
|
- Install by source (`npm:`, `git:`, `https://`, `ssh://`, `git@...`, local path)
|
|
40
41
|
- Supports direct GitHub `.ts` installs and standalone local install for self-contained packages
|
|
42
|
+
- Long-running discovery/detail screens now show dedicated loading UI, and cancellable reads can be aborted with `Esc`
|
|
41
43
|
- **Auto-update**
|
|
42
44
|
- Interactive wizard (`t` in manager, or `/extensions auto-update`)
|
|
43
45
|
- Persistent schedule restored on startup and session switch
|
|
44
|
-
- Background checks + status bar updates for installed npm packages
|
|
46
|
+
- Background checks + status bar updates for installed npm + git packages
|
|
45
47
|
- **Operational visibility**
|
|
46
48
|
- Session history (`/extensions history`)
|
|
47
49
|
- Cache controls (`/extensions clear-cache` clears persistent + runtime extmgr caches)
|
|
@@ -154,12 +156,12 @@ Examples:
|
|
|
154
156
|
|
|
155
157
|
- **Staged local changes**: Toggle local extensions on/off, then press `S` to apply all at once.
|
|
156
158
|
- **Package extension config**: Select a package and press `c` (or Enter/A → Configure) to enable/disable individual package entrypoints.
|
|
157
|
-
- After saving package extension config,
|
|
159
|
+
- After saving package extension config, run /reload to apply changes.
|
|
158
160
|
- **Two install modes**:
|
|
159
161
|
- **Managed** (npm): Auto-updates with `pi update`, stored in pi's package cache, supports Pi package manifest/convention loading
|
|
160
162
|
- **Local** (standalone): Copies to `~/.pi/agent/extensions/{package}/`, so it only accepts runnable standalone layouts (manifest-declared/root entrypoints), requires `tar` on `PATH`, and rejects packages whose runtime `dependencies` are not already bundled with the package contents
|
|
161
163
|
- **Auto-update schedule is persistent**: `/extensions auto-update 1d` stays active across future Pi sessions and is restored when switching sessions.
|
|
162
|
-
- **Auto-update
|
|
164
|
+
- **Auto-update/update badges cover npm + git packages**: extmgr now uses pi's package manager APIs for structured update detection instead of parsing `pi list` output.
|
|
163
165
|
- **Settings/cache writes are hardened**: extmgr serializes writes and uses safe file replacement to reduce JSON corruption issues.
|
|
164
166
|
- **Invalid JSON is handled safely**: malformed `auto-update.json` / metadata cache files are backed up and reset; invalid `.pi/settings.json` is not overwritten during package-extension toggles.
|
|
165
167
|
- **Reload is built-in**: When extmgr asks to reload, it calls `ctx.reload()` directly.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-extmgr",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.28",
|
|
4
4
|
"description": "Enhanced UX for managing local Pi extensions and community packages",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
@@ -19,14 +19,14 @@
|
|
|
19
19
|
"README.md"
|
|
20
20
|
],
|
|
21
21
|
"scripts": {
|
|
22
|
-
"lint": "
|
|
23
|
-
"lint:fix": "
|
|
24
|
-
"format": "
|
|
25
|
-
"format:check": "
|
|
22
|
+
"lint": "biome lint . --error-on-warnings",
|
|
23
|
+
"lint:fix": "biome check --write .",
|
|
24
|
+
"format": "biome format --write .",
|
|
25
|
+
"format:check": "biome format .",
|
|
26
26
|
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
27
27
|
"smoke-test": "node --import=tsx ./scripts/smoke-test.mjs",
|
|
28
28
|
"test": "node --import=tsx --test ./test/*.test.ts",
|
|
29
|
-
"check": "
|
|
29
|
+
"check": "tsc --noEmit -p tsconfig.json && node --import=tsx ./scripts/smoke-test.mjs && node --import=tsx --test ./test/*.test.ts && pnpm run lint && pnpm run format:check",
|
|
30
30
|
"prepublishOnly": "pnpm run check",
|
|
31
31
|
"prepare": "husky"
|
|
32
32
|
},
|
|
@@ -42,22 +42,19 @@
|
|
|
42
42
|
"@mariozechner/pi-tui": "*"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@
|
|
46
|
-
"@mariozechner/pi-
|
|
47
|
-
"@
|
|
48
|
-
"@
|
|
49
|
-
"@typescript-eslint/parser": "^8.42.0",
|
|
50
|
-
"eslint": "^9.38.0",
|
|
51
|
-
"eslint-config-prettier": "^10.1.8",
|
|
45
|
+
"@biomejs/biome": "^2.4.9",
|
|
46
|
+
"@mariozechner/pi-coding-agent": "^0.63.1",
|
|
47
|
+
"@mariozechner/pi-tui": "^0.63.1",
|
|
48
|
+
"@types/node": "^22.19.10",
|
|
52
49
|
"husky": "^9.1.7",
|
|
53
|
-
"
|
|
54
|
-
"tsx": "^4.19.3",
|
|
50
|
+
"tsx": "^4.21.0",
|
|
55
51
|
"typescript": "^5.9.3"
|
|
56
52
|
},
|
|
57
53
|
"author": "ayagmar",
|
|
58
54
|
"license": "MIT",
|
|
55
|
+
"packageManager": "pnpm@10.33.0",
|
|
59
56
|
"engines": {
|
|
60
|
-
"node": ">=22
|
|
57
|
+
"node": ">=22"
|
|
61
58
|
},
|
|
62
59
|
"repository": {
|
|
63
60
|
"type": "git",
|
package/src/commands/cache.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type ExtensionAPI, type ExtensionCommandContext } from "@mariozechner/pi-coding-agent";
|
|
2
2
|
import { clearSearchCache } from "../packages/discovery.js";
|
|
3
3
|
import { clearRemotePackageInfoCache } from "../ui/remote.js";
|
|
4
4
|
import { clearCache } from "../utils/cache.js";
|
package/src/commands/history.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type ExtensionAPI, type ExtensionCommandContext } from "@mariozechner/pi-coding-agent";
|
|
2
2
|
import {
|
|
3
|
+
type ChangeAction,
|
|
3
4
|
formatChangeEntry,
|
|
5
|
+
type HistoryFilters,
|
|
4
6
|
queryGlobalHistory,
|
|
5
7
|
querySessionChanges,
|
|
6
|
-
type ChangeAction,
|
|
7
|
-
type HistoryFilters,
|
|
8
8
|
} from "../utils/history.js";
|
|
9
9
|
import { notify } from "../utils/notify.js";
|
|
10
10
|
import { formatListOutput } from "../utils/ui-helpers.js";
|
package/src/commands/install.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import {
|
|
1
|
+
import { type ExtensionAPI, type ExtensionCommandContext } from "@mariozechner/pi-coding-agent";
|
|
2
|
+
import { type InstallScope, installPackage } from "../packages/install.js";
|
|
3
3
|
import { notify } from "../utils/notify.js";
|
|
4
4
|
|
|
5
5
|
export const INSTALL_USAGE = "Usage: /extensions install <source> [--project|--global]";
|
package/src/commands/registry.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import type
|
|
3
|
-
import { showInteractive, showInstalledPackagesLegacy, showListOnly } from "../ui/unified.js";
|
|
4
|
-
import { showRemote } from "../ui/remote.js";
|
|
1
|
+
import { type ExtensionAPI, type ExtensionCommandContext } from "@mariozechner/pi-coding-agent";
|
|
2
|
+
import { type AutocompleteItem } from "@mariozechner/pi-tui";
|
|
5
3
|
import {
|
|
6
4
|
promptRemove,
|
|
7
5
|
removePackage,
|
|
@@ -9,12 +7,14 @@ import {
|
|
|
9
7
|
updatePackage,
|
|
10
8
|
updatePackages,
|
|
11
9
|
} from "../packages/management.js";
|
|
10
|
+
import { showRemote } from "../ui/remote.js";
|
|
11
|
+
import { showInstalledPackagesLegacy, showInteractive, showListOnly } from "../ui/unified.js";
|
|
12
12
|
import { notify } from "../utils/notify.js";
|
|
13
|
-
import { handleInstallSubcommand, INSTALL_USAGE } from "./install.js";
|
|
14
|
-
import { handleHistorySubcommand } from "./history.js";
|
|
15
13
|
import { handleAutoUpdateSubcommand } from "./auto-update.js";
|
|
16
14
|
import { clearMetadataCacheCommand } from "./cache.js";
|
|
17
|
-
import
|
|
15
|
+
import { handleHistorySubcommand } from "./history.js";
|
|
16
|
+
import { handleInstallSubcommand, INSTALL_USAGE } from "./install.js";
|
|
17
|
+
import { type CommandDefinition, type CommandId } from "./types.js";
|
|
18
18
|
|
|
19
19
|
const REMOVE_USAGE = "Usage: /extensions remove <npm:package|git:url|path>";
|
|
20
20
|
|
package/src/commands/types.ts
CHANGED
|
@@ -4,12 +4,13 @@
|
|
|
4
4
|
* This module handles discovery and management of local Pi extensions
|
|
5
5
|
* in both global (~/.pi/agent/extensions) and project (.pi/extensions) scopes.
|
|
6
6
|
*/
|
|
7
|
+
|
|
8
|
+
import { type Dirent } from "node:fs";
|
|
7
9
|
import { readdir, rename, rm } from "node:fs/promises";
|
|
8
|
-
import { basename, dirname, join, relative } from "node:path";
|
|
9
10
|
import { homedir } from "node:os";
|
|
10
|
-
import
|
|
11
|
-
import type { ExtensionEntry, Scope, State } from "../types/index.js";
|
|
11
|
+
import { basename, dirname, join, relative } from "node:path";
|
|
12
12
|
import { DISABLED_SUFFIX } from "../constants.js";
|
|
13
|
+
import { type ExtensionEntry, type Scope, type State } from "../types/index.js";
|
|
13
14
|
import { fileExists, readSummary } from "../utils/fs.js";
|
|
14
15
|
|
|
15
16
|
interface RootConfig {
|
package/src/index.ts
CHANGED
|
@@ -3,21 +3,12 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Entry point - exports the main extension function
|
|
5
5
|
*/
|
|
6
|
-
import type {
|
|
7
|
-
ExtensionAPI,
|
|
8
|
-
ExtensionCommandContext,
|
|
9
|
-
ExtensionContext,
|
|
10
|
-
} from "@mariozechner/pi-coding-agent";
|
|
11
|
-
import { isPackageSource } from "./utils/format.js";
|
|
12
|
-
import { installPackage } from "./packages/install.js";
|
|
13
|
-
import { tokenizeArgs } from "./utils/command.js";
|
|
14
|
-
import { updateExtmgrStatus } from "./utils/status.js";
|
|
15
6
|
import {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
type
|
|
19
|
-
} from "
|
|
20
|
-
import {
|
|
7
|
+
type ExtensionAPI,
|
|
8
|
+
type ExtensionCommandContext,
|
|
9
|
+
type ExtensionContext,
|
|
10
|
+
} from "@mariozechner/pi-coding-agent";
|
|
11
|
+
import { createAutoUpdateNotificationHandler } from "./commands/auto-update.js";
|
|
21
12
|
import {
|
|
22
13
|
getExtensionsAutocompleteItems,
|
|
23
14
|
resolveCommand,
|
|
@@ -25,7 +16,16 @@ import {
|
|
|
25
16
|
showNonInteractiveHelp,
|
|
26
17
|
showUnknownCommandMessage,
|
|
27
18
|
} from "./commands/registry.js";
|
|
28
|
-
import {
|
|
19
|
+
import { installPackage } from "./packages/install.js";
|
|
20
|
+
import {
|
|
21
|
+
type ContextProvider,
|
|
22
|
+
startAutoUpdateTimer,
|
|
23
|
+
stopAutoUpdateTimer,
|
|
24
|
+
} from "./utils/auto-update.js";
|
|
25
|
+
import { tokenizeArgs } from "./utils/command.js";
|
|
26
|
+
import { isPackageSource } from "./utils/format.js";
|
|
27
|
+
import { getAutoUpdateConfig, hydrateAutoUpdateConfig } from "./utils/settings.js";
|
|
28
|
+
import { updateExtmgrStatus } from "./utils/status.js";
|
|
29
29
|
|
|
30
30
|
async function executeExtensionsCommand(
|
|
31
31
|
args: string,
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DefaultPackageManager,
|
|
3
|
+
getAgentDir,
|
|
4
|
+
type PackageSource,
|
|
5
|
+
type ProgressEvent,
|
|
6
|
+
SettingsManager,
|
|
7
|
+
} from "@mariozechner/pi-coding-agent";
|
|
8
|
+
import { type InstalledPackage, type Scope } from "../types/index.js";
|
|
9
|
+
import { normalizePackageIdentity, parsePackageNameAndVersion } from "../utils/package-source.js";
|
|
10
|
+
|
|
11
|
+
type PiScope = "user" | "project";
|
|
12
|
+
type PiPackageUpdate = Awaited<
|
|
13
|
+
ReturnType<DefaultPackageManager["checkForAvailableUpdates"]>
|
|
14
|
+
>[number];
|
|
15
|
+
|
|
16
|
+
export interface AvailablePackageUpdate {
|
|
17
|
+
source: string;
|
|
18
|
+
displayName: string;
|
|
19
|
+
type: "npm" | "git";
|
|
20
|
+
scope: Scope;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface PackageCatalog {
|
|
24
|
+
listInstalledPackages(options?: { dedupe?: boolean }): Promise<InstalledPackage[]>;
|
|
25
|
+
checkForAvailableUpdates(): Promise<AvailablePackageUpdate[]>;
|
|
26
|
+
install(source: string, scope: Scope, onProgress?: (event: ProgressEvent) => void): Promise<void>;
|
|
27
|
+
remove(source: string, scope: Scope, onProgress?: (event: ProgressEvent) => void): Promise<void>;
|
|
28
|
+
update(source?: string, onProgress?: (event: ProgressEvent) => void): Promise<void>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
type PackageCatalogFactory = (cwd: string) => PackageCatalog;
|
|
32
|
+
|
|
33
|
+
let packageCatalogFactory: PackageCatalogFactory = createDefaultPackageCatalog;
|
|
34
|
+
|
|
35
|
+
function toScope(scope: PiScope): Scope {
|
|
36
|
+
return scope === "project" ? "project" : "global";
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function getPackageSource(pkg: PackageSource): string {
|
|
40
|
+
return typeof pkg === "string" ? pkg : pkg.source;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function createPackageRecord(
|
|
44
|
+
source: string,
|
|
45
|
+
scope: PiScope,
|
|
46
|
+
packageManager: DefaultPackageManager
|
|
47
|
+
): InstalledPackage {
|
|
48
|
+
const resolvedPath = packageManager.getInstalledPath(source, scope);
|
|
49
|
+
const { name, version } = parsePackageNameAndVersion(source);
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
source,
|
|
53
|
+
name,
|
|
54
|
+
scope: toScope(scope),
|
|
55
|
+
...(version ? { version } : {}),
|
|
56
|
+
...(resolvedPath ? { resolvedPath } : {}),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function dedupeInstalledPackages(packages: InstalledPackage[], cwd: string): InstalledPackage[] {
|
|
61
|
+
const byIdentity = new Map<string, InstalledPackage>();
|
|
62
|
+
|
|
63
|
+
for (const pkg of packages) {
|
|
64
|
+
const baseCwd = pkg.scope === "project" ? cwd : getAgentDir();
|
|
65
|
+
const identity = normalizePackageIdentity(pkg.source, {
|
|
66
|
+
...(pkg.resolvedPath ? { resolvedPath: pkg.resolvedPath } : {}),
|
|
67
|
+
cwd: baseCwd,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
if (!byIdentity.has(identity)) {
|
|
71
|
+
byIdentity.set(identity, pkg);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return [...byIdentity.values()];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function setProgressCallback(
|
|
79
|
+
packageManager: DefaultPackageManager,
|
|
80
|
+
onProgress?: (event: ProgressEvent) => void
|
|
81
|
+
): void {
|
|
82
|
+
packageManager.setProgressCallback(onProgress);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function createDefaultPackageCatalog(cwd: string): PackageCatalog {
|
|
86
|
+
const agentDir = getAgentDir();
|
|
87
|
+
const settingsManager = SettingsManager.create(cwd, agentDir);
|
|
88
|
+
const packageManager = new DefaultPackageManager({ cwd, agentDir, settingsManager });
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
listInstalledPackages(options) {
|
|
92
|
+
const projectPackages = (settingsManager.getProjectSettings().packages ?? []).map((pkg) =>
|
|
93
|
+
createPackageRecord(getPackageSource(pkg), "project", packageManager)
|
|
94
|
+
);
|
|
95
|
+
const globalPackages = (settingsManager.getGlobalSettings().packages ?? []).map((pkg) =>
|
|
96
|
+
createPackageRecord(getPackageSource(pkg), "user", packageManager)
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
const installed = [...projectPackages, ...globalPackages];
|
|
100
|
+
return Promise.resolve(
|
|
101
|
+
options?.dedupe === false ? installed : dedupeInstalledPackages(installed, cwd)
|
|
102
|
+
);
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
async checkForAvailableUpdates() {
|
|
106
|
+
const updates = await packageManager.checkForAvailableUpdates();
|
|
107
|
+
return updates.map((update: PiPackageUpdate) => ({
|
|
108
|
+
source: update.source,
|
|
109
|
+
displayName: update.displayName,
|
|
110
|
+
type: update.type,
|
|
111
|
+
scope: toScope(update.scope),
|
|
112
|
+
}));
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
async install(source, scope, onProgress) {
|
|
116
|
+
setProgressCallback(packageManager, onProgress);
|
|
117
|
+
|
|
118
|
+
try {
|
|
119
|
+
await packageManager.install(source, { local: scope === "project" });
|
|
120
|
+
packageManager.addSourceToSettings(source, { local: scope === "project" });
|
|
121
|
+
await settingsManager.flush();
|
|
122
|
+
} finally {
|
|
123
|
+
setProgressCallback(packageManager, undefined);
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
async remove(source, scope, onProgress) {
|
|
128
|
+
setProgressCallback(packageManager, onProgress);
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
await packageManager.remove(source, { local: scope === "project" });
|
|
132
|
+
const removed = packageManager.removeSourceFromSettings(source, {
|
|
133
|
+
local: scope === "project",
|
|
134
|
+
});
|
|
135
|
+
await settingsManager.flush();
|
|
136
|
+
|
|
137
|
+
if (!removed) {
|
|
138
|
+
throw new Error(`No matching package found for ${source}`);
|
|
139
|
+
}
|
|
140
|
+
} finally {
|
|
141
|
+
setProgressCallback(packageManager, undefined);
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
async update(source, onProgress) {
|
|
146
|
+
setProgressCallback(packageManager, onProgress);
|
|
147
|
+
|
|
148
|
+
try {
|
|
149
|
+
await packageManager.update(source);
|
|
150
|
+
} finally {
|
|
151
|
+
setProgressCallback(packageManager, undefined);
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function getPackageCatalog(cwd: string): PackageCatalog {
|
|
158
|
+
return packageCatalogFactory(cwd);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function setPackageCatalogFactory(factory?: PackageCatalogFactory): void {
|
|
162
|
+
packageCatalogFactory = factory ?? createDefaultPackageCatalog;
|
|
163
|
+
}
|