10
package-lock.json
generated
10
package-lock.json
generated
@@ -2337,6 +2337,13 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/semver": {
|
||||||
|
"version": "7.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.1.tgz",
|
||||||
|
"integrity": "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@typescript/native-preview": {
|
"node_modules/@typescript/native-preview": {
|
||||||
"version": "7.0.0-dev.20260120.1",
|
"version": "7.0.0-dev.20260120.1",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript/native-preview/-/native-preview-7.0.0-dev.20260120.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript/native-preview/-/native-preview-7.0.0-dev.20260120.1.tgz",
|
||||||
@@ -4700,7 +4707,6 @@
|
|||||||
"version": "7.8.0",
|
"version": "7.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz",
|
||||||
"integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==",
|
"integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==",
|
||||||
"dev": true,
|
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"bin": {
|
"bin": {
|
||||||
"semver": "bin/semver.js"
|
"semver": "bin/semver.js"
|
||||||
@@ -6269,6 +6275,7 @@
|
|||||||
"jiti": "2.7.0",
|
"jiti": "2.7.0",
|
||||||
"minimatch": "10.2.5",
|
"minimatch": "10.2.5",
|
||||||
"proper-lockfile": "4.1.2",
|
"proper-lockfile": "4.1.2",
|
||||||
|
"semver": "7.8.0",
|
||||||
"typebox": "1.1.38",
|
"typebox": "1.1.38",
|
||||||
"undici": "8.3.0",
|
"undici": "8.3.0",
|
||||||
"yaml": "2.9.0"
|
"yaml": "2.9.0"
|
||||||
@@ -6283,6 +6290,7 @@
|
|||||||
"@types/ms": "2.1.0",
|
"@types/ms": "2.1.0",
|
||||||
"@types/node": "24.12.4",
|
"@types/node": "24.12.4",
|
||||||
"@types/proper-lockfile": "4.1.4",
|
"@types/proper-lockfile": "4.1.4",
|
||||||
|
"@types/semver": "7.7.1",
|
||||||
"shx": "0.4.0",
|
"shx": "0.4.0",
|
||||||
"typescript": "5.9.3",
|
"typescript": "5.9.3",
|
||||||
"vitest": "3.2.4"
|
"vitest": "3.2.4"
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixed `pi update` for pnpm global installs whose configured `global-bin-dir` no longer matches the active pnpm home ([#5689](https://github.com/earendil-works/pi/issues/5689)).
|
- Fixed `pi update` for pnpm global installs whose configured `global-bin-dir` no longer matches the active pnpm home ([#5689](https://github.com/earendil-works/pi/issues/5689)).
|
||||||
|
- Fixed npm package specs that use ranges or tags (for example `@^1.2.7`) so installed package resources still load instead of being treated as mismatched exact pins ([#5695](https://github.com/earendil-works/pi/issues/5695)).
|
||||||
- Fixed inherited OpenCode/OpenCode Go completion model metadata to omit long-retention cache fields for routes that reject `prompt_cache_retention` ([#5702](https://github.com/earendil-works/pi/issues/5702)).
|
- Fixed inherited OpenCode/OpenCode Go completion model metadata to omit long-retention cache fields for routes that reject `prompt_cache_retention` ([#5702](https://github.com/earendil-works/pi/issues/5702)).
|
||||||
- Fixed custom provider config so plain uppercase API key and header values remain literals instead of being treated as legacy environment references; use explicit `$ENV_VAR` syntax for environment variables ([#5661](https://github.com/earendil-works/pi/issues/5661)).
|
- Fixed custom provider config so plain uppercase API key and header values remain literals instead of being treated as legacy environment references; use explicit `$ENV_VAR` syntax for environment variables ([#5661](https://github.com/earendil-works/pi/issues/5661)).
|
||||||
|
|
||||||
|
|||||||
13
packages/coding-agent/npm-shrinkwrap.json
generated
13
packages/coding-agent/npm-shrinkwrap.json
generated
@@ -23,6 +23,7 @@
|
|||||||
"jiti": "2.7.0",
|
"jiti": "2.7.0",
|
||||||
"minimatch": "10.2.5",
|
"minimatch": "10.2.5",
|
||||||
"proper-lockfile": "4.1.2",
|
"proper-lockfile": "4.1.2",
|
||||||
|
"semver": "7.8.0",
|
||||||
"typebox": "1.1.38",
|
"typebox": "1.1.38",
|
||||||
"undici": "8.3.0",
|
"undici": "8.3.0",
|
||||||
"yaml": "2.9.0"
|
"yaml": "2.9.0"
|
||||||
@@ -1636,6 +1637,18 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"node_modules/semver": {
|
||||||
|
"version": "7.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz",
|
||||||
|
"integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==",
|
||||||
|
"license": "ISC",
|
||||||
|
"bin": {
|
||||||
|
"semver": "bin/semver.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/shebang-command": {
|
"node_modules/shebang-command": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||||
|
|||||||
@@ -50,6 +50,7 @@
|
|||||||
"jiti": "2.7.0",
|
"jiti": "2.7.0",
|
||||||
"minimatch": "10.2.5",
|
"minimatch": "10.2.5",
|
||||||
"proper-lockfile": "4.1.2",
|
"proper-lockfile": "4.1.2",
|
||||||
|
"semver": "7.8.0",
|
||||||
"typebox": "1.1.38",
|
"typebox": "1.1.38",
|
||||||
"undici": "8.3.0",
|
"undici": "8.3.0",
|
||||||
"yaml": "2.9.0"
|
"yaml": "2.9.0"
|
||||||
@@ -70,6 +71,7 @@
|
|||||||
"@types/ms": "2.1.0",
|
"@types/ms": "2.1.0",
|
||||||
"@types/node": "24.12.4",
|
"@types/node": "24.12.4",
|
||||||
"@types/proper-lockfile": "4.1.4",
|
"@types/proper-lockfile": "4.1.4",
|
||||||
|
"@types/semver": "7.7.1",
|
||||||
"shx": "0.4.0",
|
"shx": "0.4.0",
|
||||||
"typescript": "5.9.3",
|
"typescript": "5.9.3",
|
||||||
"vitest": "3.2.4"
|
"vitest": "3.2.4"
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import type { Readable } from "node:stream";
|
|||||||
import { globSync } from "glob";
|
import { globSync } from "glob";
|
||||||
import ignore from "ignore";
|
import ignore from "ignore";
|
||||||
import { minimatch } from "minimatch";
|
import { minimatch } from "minimatch";
|
||||||
|
import { maxSatisfying, rcompare, satisfies, valid, validRange } from "semver";
|
||||||
import { CONFIG_DIR_NAME } from "../config.ts";
|
import { CONFIG_DIR_NAME } from "../config.ts";
|
||||||
import { spawnProcess, spawnProcessSync } from "../utils/child-process.ts";
|
import { spawnProcess, spawnProcessSync } from "../utils/child-process.ts";
|
||||||
import { type GitSource, parseGitUrl } from "../utils/git.ts";
|
import { type GitSource, parseGitUrl } from "../utils/git.ts";
|
||||||
@@ -44,6 +45,14 @@ function isOfflineModeEnabled(): boolean {
|
|||||||
return value === "1" || value.toLowerCase() === "true" || value.toLowerCase() === "yes";
|
return value === "1" || value.toLowerCase() === "true" || value.toLowerCase() === "yes";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isExactNpmVersion(version: string | undefined): boolean {
|
||||||
|
return valid(version ?? "") !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getNpmVersionRange(version: string | undefined): string | undefined {
|
||||||
|
return version ? (validRange(version) ?? undefined) : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
export interface PathMetadata {
|
export interface PathMetadata {
|
||||||
source: string;
|
source: string;
|
||||||
scope: SourceScope;
|
scope: SourceScope;
|
||||||
@@ -119,6 +128,8 @@ type NpmSource = {
|
|||||||
type: "npm";
|
type: "npm";
|
||||||
spec: string;
|
spec: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
version?: string;
|
||||||
|
range?: string;
|
||||||
pinned: boolean;
|
pinned: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1113,8 +1124,8 @@ export class DefaultPackageManager implements PackageManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const latestVersion = await this.getLatestNpmVersion(source.name);
|
const targetVersion = await this.getLatestNpmVersion(source.version ? source.spec : source.name, source.range);
|
||||||
return latestVersion !== installedVersion;
|
return targetVersion !== installedVersion;
|
||||||
} catch {
|
} catch {
|
||||||
// Preserve existing update behavior when version lookup fails.
|
// Preserve existing update behavior when version lookup fails.
|
||||||
return true;
|
return true;
|
||||||
@@ -1128,7 +1139,7 @@ export class DefaultPackageManager implements PackageManager {
|
|||||||
|
|
||||||
const sourceLabel = sources.length === 1 ? sources[0].source : `${scope} npm packages`;
|
const sourceLabel = sources.length === 1 ? sources[0].source : `${scope} npm packages`;
|
||||||
const message = sources.length === 1 ? `Updating ${sources[0].source}...` : `Updating ${scope} npm packages...`;
|
const message = sources.length === 1 ? `Updating ${sources[0].source}...` : `Updating ${scope} npm packages...`;
|
||||||
const specs = sources.map((entry) => `${entry.parsed.name}@latest`);
|
const specs = sources.map((entry) => (entry.parsed.version ? entry.parsed.spec : `${entry.parsed.name}@latest`));
|
||||||
|
|
||||||
await this.withProgress("update", sourceLabel, message, async () => {
|
await this.withProgress("update", sourceLabel, message, async () => {
|
||||||
await this.installNpmBatch(specs, scope);
|
await this.installNpmBatch(specs, scope);
|
||||||
@@ -1241,8 +1252,7 @@ export class DefaultPackageManager implements PackageManager {
|
|||||||
if (parsed.type === "npm") {
|
if (parsed.type === "npm") {
|
||||||
let installedPath = this.getNpmInstallPath(parsed, scope);
|
let installedPath = this.getNpmInstallPath(parsed, scope);
|
||||||
const needsInstall =
|
const needsInstall =
|
||||||
!existsSync(installedPath) ||
|
!existsSync(installedPath) || !(await this.installedNpmMatchesConfiguredVersion(parsed, installedPath));
|
||||||
(parsed.pinned && !(await this.installedNpmMatchesPinnedVersion(parsed, installedPath)));
|
|
||||||
if (needsInstall) {
|
if (needsInstall) {
|
||||||
const installed = await installMissing();
|
const installed = await installMissing();
|
||||||
if (!installed) continue;
|
if (!installed) continue;
|
||||||
@@ -1394,7 +1404,9 @@ export class DefaultPackageManager implements PackageManager {
|
|||||||
type: "npm",
|
type: "npm",
|
||||||
spec,
|
spec,
|
||||||
name,
|
name,
|
||||||
pinned: Boolean(version),
|
version,
|
||||||
|
range: getNpmVersionRange(version),
|
||||||
|
pinned: isExactNpmVersion(version),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1411,18 +1423,12 @@ export class DefaultPackageManager implements PackageManager {
|
|||||||
return { type: "local", path: source };
|
return { type: "local", path: source };
|
||||||
}
|
}
|
||||||
|
|
||||||
private async installedNpmMatchesPinnedVersion(source: NpmSource, installedPath: string): Promise<boolean> {
|
private async installedNpmMatchesConfiguredVersion(source: NpmSource, installedPath: string): Promise<boolean> {
|
||||||
const installedVersion = this.getInstalledNpmVersion(installedPath);
|
const installedVersion = this.getInstalledNpmVersion(installedPath);
|
||||||
if (!installedVersion) {
|
if (!installedVersion) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
return source.range ? satisfies(installedVersion, source.range) : true;
|
||||||
const { version: pinnedVersion } = this.parseNpmSpec(source.spec);
|
|
||||||
if (!pinnedVersion) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return installedVersion === pinnedVersion;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async npmHasAvailableUpdate(source: NpmSource, installedPath: string): Promise<boolean> {
|
private async npmHasAvailableUpdate(source: NpmSource, installedPath: string): Promise<boolean> {
|
||||||
@@ -1436,8 +1442,8 @@ export class DefaultPackageManager implements PackageManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const latestVersion = await this.getLatestNpmVersion(source.name);
|
const targetVersion = await this.getLatestNpmVersion(source.version ? source.spec : source.name, source.range);
|
||||||
return latestVersion !== installedVersion;
|
return targetVersion !== installedVersion;
|
||||||
} catch {
|
} catch {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1455,16 +1461,25 @@ export class DefaultPackageManager implements PackageManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getLatestNpmVersion(packageName: string): Promise<string> {
|
private async getLatestNpmVersion(packageSpec: string, range?: string): Promise<string> {
|
||||||
const npmCommand = this.getNpmCommand();
|
const npmCommand = this.getNpmCommand();
|
||||||
const stdout = await this.runCommandCapture(
|
const stdout = await this.runCommandCapture(
|
||||||
npmCommand.command,
|
npmCommand.command,
|
||||||
[...npmCommand.args, "view", packageName, "version", "--json"],
|
[...npmCommand.args, "view", packageSpec, "version", "--json"],
|
||||||
{ cwd: this.cwd, timeoutMs: NETWORK_TIMEOUT_MS },
|
{ cwd: this.cwd, timeoutMs: NETWORK_TIMEOUT_MS },
|
||||||
);
|
);
|
||||||
const raw = stdout.trim();
|
const raw = stdout.trim();
|
||||||
if (!raw) throw new Error("Empty response from npm view");
|
if (!raw) throw new Error("Empty response from npm view");
|
||||||
return JSON.parse(raw);
|
const parsed = JSON.parse(raw) as unknown;
|
||||||
|
if (typeof parsed === "string") {
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
if (Array.isArray(parsed)) {
|
||||||
|
const versions = parsed.filter((value): value is string => typeof value === "string" && value.length > 0);
|
||||||
|
const latest = range ? maxSatisfying(versions, range) : [...versions].sort(rcompare)[0];
|
||||||
|
if (latest) return latest;
|
||||||
|
}
|
||||||
|
throw new Error("Unexpected response from npm view");
|
||||||
}
|
}
|
||||||
|
|
||||||
private async gitHasAvailableUpdate(installedPath: string): Promise<boolean> {
|
private async gitHasAvailableUpdate(installedPath: string): Promise<boolean> {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { compare, valid } from "semver";
|
||||||
import { getPiUserAgent } from "./pi-user-agent.ts";
|
import { getPiUserAgent } from "./pi-user-agent.ts";
|
||||||
|
|
||||||
const LATEST_VERSION_URL = "https://pi.dev/api/latest-version";
|
const LATEST_VERSION_URL = "https://pi.dev/api/latest-version";
|
||||||
@@ -9,40 +10,13 @@ export interface LatestPiRelease {
|
|||||||
note?: string;
|
note?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ParsedVersion {
|
|
||||||
major: number;
|
|
||||||
minor: number;
|
|
||||||
patch: number;
|
|
||||||
prerelease?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
function parsePackageVersion(version: string): ParsedVersion | undefined {
|
|
||||||
const match = version.trim().match(/^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+.*)?$/);
|
|
||||||
if (!match) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
major: Number.parseInt(match[1], 10),
|
|
||||||
minor: Number.parseInt(match[2], 10),
|
|
||||||
patch: Number.parseInt(match[3], 10),
|
|
||||||
prerelease: match[4],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function comparePackageVersions(leftVersion: string, rightVersion: string): number | undefined {
|
export function comparePackageVersions(leftVersion: string, rightVersion: string): number | undefined {
|
||||||
const left = parsePackageVersion(leftVersion);
|
const left = valid(leftVersion.trim());
|
||||||
const right = parsePackageVersion(rightVersion);
|
const right = valid(rightVersion.trim());
|
||||||
if (!left || !right) {
|
if (!left || !right) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
return compare(left, right);
|
||||||
if (left.major !== right.major) return left.major - right.major;
|
|
||||||
if (left.minor !== right.minor) return left.minor - right.minor;
|
|
||||||
if (left.patch !== right.patch) return left.patch - right.patch;
|
|
||||||
if (left.prerelease === right.prerelease) return 0;
|
|
||||||
if (!left.prerelease) return 1;
|
|
||||||
if (!right.prerelease) return -1;
|
|
||||||
return left.prerelease.localeCompare(right.prerelease);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isNewerPackageVersion(candidateVersion: string, currentVersion: string): boolean {
|
export function isNewerPackageVersion(candidateVersion: string, currentVersion: string): boolean {
|
||||||
|
|||||||
@@ -1128,8 +1128,17 @@ Content`,
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should parse package source types from docs examples", () => {
|
it("should parse package source types from docs examples", () => {
|
||||||
expect((packageManager as any).parseSource("npm:@scope/pkg@1.2.3").type).toBe("npm");
|
const parseNpm = (source: string) => {
|
||||||
expect((packageManager as any).parseSource("npm:pkg").type).toBe("npm");
|
const parsed = (packageManager as any).parseSource(source);
|
||||||
|
if (parsed.type !== "npm") {
|
||||||
|
throw new Error(`Expected npm source: ${source}`);
|
||||||
|
}
|
||||||
|
return parsed;
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(parseNpm("npm:@scope/pkg@1.2.3").pinned).toBe(true);
|
||||||
|
expect(parseNpm("npm:@scope/pkg@^1.2.3").pinned).toBe(false);
|
||||||
|
expect(parseNpm("npm:pkg").pinned).toBe(false);
|
||||||
|
|
||||||
expect((packageManager as any).parseSource("git:github.com/user/repo@v1").type).toBe("git");
|
expect((packageManager as any).parseSource("git:github.com/user/repo@v1").type).toBe("git");
|
||||||
expect((packageManager as any).parseSource("https://github.com/user/repo@v1").type).toBe("git");
|
expect((packageManager as any).parseSource("https://github.com/user/repo@v1").type).toBe("git");
|
||||||
@@ -2052,25 +2061,27 @@ export default function(api) { api.registerTool({ name: "test", description: "te
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("offline mode and network timeouts", () => {
|
describe("offline mode and network timeouts", () => {
|
||||||
it("should update project npm packages using @latest when newer version is available", async () => {
|
it("should update npm range packages using the configured spec", async () => {
|
||||||
const installedPath = join(tempDir, ".pi", "npm", "node_modules", "example");
|
const installedPath = join(tempDir, ".pi", "npm", "node_modules", "example");
|
||||||
mkdirSync(installedPath, { recursive: true });
|
mkdirSync(installedPath, { recursive: true });
|
||||||
writeFileSync(join(installedPath, "package.json"), JSON.stringify({ name: "example", version: "1.0.0" }));
|
writeFileSync(join(installedPath, "package.json"), JSON.stringify({ name: "example", version: "1.0.0" }));
|
||||||
settingsManager.setProjectPackages(["npm:example"]);
|
settingsManager.setProjectPackages(["npm:example@^1.0.0"]);
|
||||||
|
|
||||||
const runCommandCaptureSpy = vi.spyOn(packageManager as any, "runCommandCapture").mockResolvedValue('"1.2.3"');
|
const runCommandCaptureSpy = vi
|
||||||
|
.spyOn(packageManager as any, "runCommandCapture")
|
||||||
|
.mockResolvedValue('["1.0.0","1.2.0"]');
|
||||||
const runCommandSpy = vi.spyOn(packageManager as any, "runCommand").mockResolvedValue(undefined);
|
const runCommandSpy = vi.spyOn(packageManager as any, "runCommand").mockResolvedValue(undefined);
|
||||||
|
|
||||||
await packageManager.update("npm:example");
|
await packageManager.update("npm:example");
|
||||||
|
|
||||||
expect(runCommandCaptureSpy).toHaveBeenCalledWith(
|
expect(runCommandCaptureSpy).toHaveBeenCalledWith(
|
||||||
"npm",
|
"npm",
|
||||||
["view", "example", "version", "--json"],
|
["view", "example@^1.0.0", "version", "--json"],
|
||||||
expect.objectContaining({ cwd: tempDir, timeoutMs: expect.any(Number) }),
|
expect.objectContaining({ cwd: tempDir, timeoutMs: expect.any(Number) }),
|
||||||
);
|
);
|
||||||
expect(runCommandSpy).toHaveBeenCalledWith(
|
expect(runCommandSpy).toHaveBeenCalledWith(
|
||||||
"npm",
|
"npm",
|
||||||
["install", "example@latest", "--prefix", join(tempDir, ".pi", "npm"), "--legacy-peer-deps"],
|
["install", "example@^1.0.0", "--prefix", join(tempDir, ".pi", "npm"), "--legacy-peer-deps"],
|
||||||
undefined,
|
undefined,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -2078,17 +2089,19 @@ export default function(api) { api.registerTool({ name: "test", description: "te
|
|||||||
it("should skip project npm update when installed version matches latest", async () => {
|
it("should skip project npm update when installed version matches latest", async () => {
|
||||||
const installedPath = join(tempDir, ".pi", "npm", "node_modules", "example");
|
const installedPath = join(tempDir, ".pi", "npm", "node_modules", "example");
|
||||||
mkdirSync(installedPath, { recursive: true });
|
mkdirSync(installedPath, { recursive: true });
|
||||||
writeFileSync(join(installedPath, "package.json"), JSON.stringify({ name: "example", version: "1.2.3" }));
|
writeFileSync(join(installedPath, "package.json"), JSON.stringify({ name: "example", version: "1.3.1" }));
|
||||||
settingsManager.setProjectPackages(["npm:example"]);
|
settingsManager.setProjectPackages(["npm:example@^1.0.0"]);
|
||||||
|
|
||||||
const runCommandCaptureSpy = vi.spyOn(packageManager as any, "runCommandCapture").mockResolvedValue('"1.2.3"');
|
const runCommandCaptureSpy = vi
|
||||||
|
.spyOn(packageManager as any, "runCommandCapture")
|
||||||
|
.mockResolvedValue('["1.0.0","1.3.1","1.0.2"]');
|
||||||
const runCommandSpy = vi.spyOn(packageManager as any, "runCommand").mockResolvedValue(undefined);
|
const runCommandSpy = vi.spyOn(packageManager as any, "runCommand").mockResolvedValue(undefined);
|
||||||
|
|
||||||
await packageManager.update("npm:example");
|
await packageManager.update("npm:example");
|
||||||
|
|
||||||
expect(runCommandCaptureSpy).toHaveBeenCalledWith(
|
expect(runCommandCaptureSpy).toHaveBeenCalledWith(
|
||||||
"npm",
|
"npm",
|
||||||
["view", "example", "version", "--json"],
|
["view", "example@^1.0.0", "version", "--json"],
|
||||||
expect.objectContaining({ cwd: tempDir, timeoutMs: expect.any(Number) }),
|
expect.objectContaining({ cwd: tempDir, timeoutMs: expect.any(Number) }),
|
||||||
);
|
);
|
||||||
expect(runCommandSpy).not.toHaveBeenCalled();
|
expect(runCommandSpy).not.toHaveBeenCalled();
|
||||||
@@ -2298,11 +2311,12 @@ export default function(api) { api.registerTool({ name: "test", description: "te
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should not run npm view during resolve for installed unpinned packages", async () => {
|
it("should not run npm view during resolve for installed unpinned packages", async () => {
|
||||||
|
process.env.PI_OFFLINE = "1";
|
||||||
const installedPath = join(tempDir, ".pi", "npm", "node_modules", "example");
|
const installedPath = join(tempDir, ".pi", "npm", "node_modules", "example");
|
||||||
mkdirSync(join(installedPath, "extensions"), { recursive: true });
|
mkdirSync(join(installedPath, "extensions"), { recursive: true });
|
||||||
writeFileSync(join(installedPath, "package.json"), JSON.stringify({ name: "example", version: "1.0.0" }));
|
writeFileSync(join(installedPath, "package.json"), JSON.stringify({ name: "example", version: "1.0.0" }));
|
||||||
writeFileSync(join(installedPath, "extensions", "index.ts"), "export default function() {};");
|
writeFileSync(join(installedPath, "extensions", "index.ts"), "export default function() {};");
|
||||||
settingsManager.setProjectPackages(["npm:example"]);
|
settingsManager.setProjectPackages(["npm:example@^1.0.0"]);
|
||||||
|
|
||||||
const runCommandCaptureSpy = vi.spyOn(packageManager as any, "runCommandCapture");
|
const runCommandCaptureSpy = vi.spyOn(packageManager as any, "runCommandCapture");
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ describe("version checks", () => {
|
|||||||
expect(comparePackageVersions("0.70.6", "0.70.5")).toBeGreaterThan(0);
|
expect(comparePackageVersions("0.70.6", "0.70.5")).toBeGreaterThan(0);
|
||||||
expect(comparePackageVersions("0.70.5", "0.70.5")).toBe(0);
|
expect(comparePackageVersions("0.70.5", "0.70.5")).toBe(0);
|
||||||
expect(comparePackageVersions("0.70.4", "0.70.5")).toBeLessThan(0);
|
expect(comparePackageVersions("0.70.4", "0.70.5")).toBeLessThan(0);
|
||||||
|
expect(comparePackageVersions("5.0.0-beta.20", "5.0.0-beta.9")).toBeGreaterThan(0);
|
||||||
expect(isNewerPackageVersion("0.70.5", "0.70.5")).toBe(false);
|
expect(isNewerPackageVersion("0.70.5", "0.70.5")).toBe(false);
|
||||||
expect(isNewerPackageVersion("0.70.6", "0.70.5")).toBe(true);
|
expect(isNewerPackageVersion("0.70.6", "0.70.5")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user