Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions apps/cli/src/commands/self/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
detectInstallScope,
detectPackageManager,
fetchLatestVersion,
getDistTagForVersion,
performSelfUpdate,
} from '../../self-update.js';

Expand Down Expand Up @@ -33,10 +34,11 @@ const updateCommand = command({
}

const currentVersion = packageJson.version;
const distTag = getDistTagForVersion(currentVersion);
console.log(`Current version: ${currentVersion}`);
console.log('Checking for updates...');

const latestVersion = await fetchLatestVersion();
const latestVersion = await fetchLatestVersion(distTag);
if (latestVersion && latestVersion === currentVersion) {
console.log(`Already up to date (${currentVersion}).`);
return;
Expand All @@ -49,7 +51,7 @@ const updateCommand = command({
const scopeLabel = scope === 'local' ? 'local project install' : 'global install';
console.log(`Updating agentv using ${pm} (${scopeLabel})...\n`);

const result = await performSelfUpdate({ pm, currentVersion, scope });
const result = await performSelfUpdate({ pm, currentVersion, versionRange: distTag, scope });

if (!result.success) {
console.error('\nUpdate failed.');
Expand Down
20 changes: 14 additions & 6 deletions apps/cli/src/self-update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
* version specifier (e.g., `agentv@">=4.1.0"`). This ensures the update
* respects the project's constraints and avoids unintended major-version jumps.
*
* When called from `agentv self update` (no range), it installs `@latest`.
* When called from `agentv self update` (no range), it installs `@latest` for stable
* versions or `@next` when the current version has a prerelease identifier.
*
* Install scope detection: if `process.argv[1]` contains `node_modules`,
* agentv was invoked from a local project dependency (e.g. `npx agentv` or
Expand All @@ -30,7 +31,12 @@ import { existsSync } from 'node:fs';
import { get } from 'node:https';
import { basename, dirname, join, win32 } from 'node:path';

const NPM_REGISTRY_URL = 'https://registry.npmjs.org/agentv/latest';
const NPM_REGISTRY_BASE = 'https://registry.npmjs.org/agentv/';

/** Returns 'next' if the version has a prerelease identifier, 'latest' otherwise. */
export function getDistTagForVersion(version: string): 'next' | 'latest' {
return version.includes('-') ? 'next' : 'latest';
}

/**
* Detect package manager from the script path.
Expand Down Expand Up @@ -111,11 +117,11 @@ function runCommand(cmd: string, args: string[]): Promise<{ exitCode: number; st

/**
* Fetch the latest published version of agentv from the npm registry.
* Returns null on network errors or timeouts (best-effort).
* Pass distTag='next' for prerelease channels. Returns null on network errors.
*/
export function fetchLatestVersion(): Promise<string | null> {
export function fetchLatestVersion(distTag: 'latest' | 'next' = 'latest'): Promise<string | null> {
return new Promise((resolve) => {
const req = get(NPM_REGISTRY_URL, { timeout: 5000 }, (res) => {
const req = get(`${NPM_REGISTRY_BASE}${distTag}`, { timeout: 5000 }, (res) => {
if (res.statusCode !== 200) {
res.resume();
resolve(null);
Expand Down Expand Up @@ -226,7 +232,9 @@ export async function performSelfUpdate(options?: {
}> {
const pm = options?.pm ?? detectPackageManager();
const currentVersion = options?.currentVersion ?? 'unknown';
const versionSpec = options?.versionRange ?? 'latest';
const versionSpec =
options?.versionRange ??
getDistTagForVersion(currentVersion === 'unknown' ? '' : currentVersion);
const scope = options?.scope ?? detectInstallScope();

const args = getInstallArgs(pm, versionSpec, scope);
Expand Down
10 changes: 6 additions & 4 deletions apps/cli/src/update-check.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { getAgentvConfigDir } from '@agentv/core';
const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24 hours
const CONFIG_DIR = getAgentvConfigDir();
const CACHE_FILE = 'version-check.json';
const NPM_REGISTRY_URL = 'https://registry.npmjs.org/agentv/latest';
const NPM_REGISTRY_BASE = 'https://registry.npmjs.org/agentv/';

export interface UpdateCache {
latestVersion: string;
Expand Down Expand Up @@ -66,16 +66,17 @@ export function buildNotice(currentVersion: string, latestVersion: string | null
* fetches latest version from npm and updates the cache file. The child
* survives even if the parent calls process.exit().
*/
export function backgroundUpdateCheck(): void {
export function backgroundUpdateCheck(distTag: 'latest' | 'next' = 'latest'): void {
const dir = CONFIG_DIR;
const filePath = join(dir, CACHE_FILE);
const registryUrl = `${NPM_REGISTRY_BASE}${distTag}`;

const script = `
const https = require('https');
const fs = require('fs');
const dir = ${JSON.stringify(dir)};
const filePath = ${JSON.stringify(filePath)};
https.get(${JSON.stringify(NPM_REGISTRY_URL)}, { timeout: 5000 }, (res) => {
https.get(${JSON.stringify(registryUrl)}, { timeout: 5000 }, (res) => {
if (res.statusCode !== 200) { res.resume(); process.exit(); }
let body = '';
res.on('data', (c) => body += c);
Expand Down Expand Up @@ -110,9 +111,10 @@ export async function getUpdateNotice(currentVersion: string): Promise<string |
if (process.env.AGENTV_NO_UPDATE_CHECK === '1' || process.env.CI === 'true') {
return null;
}
const distTag = currentVersion.includes('-') ? 'next' : 'latest';
const cache = await getCachedUpdateInfo();
if (shouldCheck(cache)) {
backgroundUpdateCheck();
backgroundUpdateCheck(distTag);
}
return buildNotice(currentVersion, cache?.latestVersion ?? null);
}
Loading