agentloom 0.1.3 → 0.1.4
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 +11 -5
- package/dist/cli.js +13 -8
- package/dist/commands/upgrade.d.ts +2 -0
- package/dist/commands/upgrade.js +19 -0
- package/dist/core/copy.d.ts +1 -0
- package/dist/core/copy.js +13 -0
- package/dist/core/lockfile.js +34 -2
- package/dist/core/manage-agents-bootstrap.js +1 -0
- package/dist/core/router.d.ts +1 -1
- package/dist/core/router.js +1 -0
- package/dist/core/version-notifier.d.ts +11 -1
- package/dist/core/version-notifier.js +179 -44
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -55,6 +55,7 @@ Global scope uses `~/.agents` with the same file layout.
|
|
|
55
55
|
- `agentloom add <source>`
|
|
56
56
|
- `agentloom find <query>`
|
|
57
57
|
- `agentloom update [source]`
|
|
58
|
+
- `agentloom upgrade`
|
|
58
59
|
- `agentloom sync`
|
|
59
60
|
- `agentloom delete <source|name>`
|
|
60
61
|
|
|
@@ -118,6 +119,7 @@ agentloom --help
|
|
|
118
119
|
agentloom find --help
|
|
119
120
|
agentloom add --help
|
|
120
121
|
agentloom update --help
|
|
122
|
+
agentloom upgrade --help
|
|
121
123
|
agentloom sync --help
|
|
122
124
|
agentloom delete --help
|
|
123
125
|
agentloom agent --help
|
|
@@ -131,12 +133,16 @@ agentloom mcp server --help
|
|
|
131
133
|
|
|
132
134
|
### Version update notice
|
|
133
135
|
|
|
134
|
-
`agentloom`
|
|
136
|
+
`agentloom` performs best-effort npm version checks and upgrades when a newer release is available.
|
|
135
137
|
|
|
136
|
-
-
|
|
137
|
-
- check
|
|
138
|
-
-
|
|
139
|
-
-
|
|
138
|
+
- checks are cached (`~/.agents/.agentloom-version-cache.json`)
|
|
139
|
+
- check/upgrade attempts run at most once every 2 hours per detected version
|
|
140
|
+
- in interactive TTY sessions, agentloom asks before upgrading
|
|
141
|
+
- in non-interactive sessions, upgrades run without prompts
|
|
142
|
+
- after an approved upgrade, the original command is re-run automatically
|
|
143
|
+
- if running from `npx`, auto-upgrade re-runs with `npx agentloom@<latest>`
|
|
144
|
+
- manual upgrade command: `agentloom upgrade`
|
|
145
|
+
- disable auto checks via:
|
|
140
146
|
|
|
141
147
|
```bash
|
|
142
148
|
AGENTLOOM_DISABLE_UPDATE_NOTIFIER=1
|
package/dist/cli.js
CHANGED
|
@@ -9,6 +9,7 @@ import { runInitCommand } from "./commands/init.js";
|
|
|
9
9
|
import { runMcpCommand } from "./commands/mcp.js";
|
|
10
10
|
import { runSkillCommand } from "./commands/skills.js";
|
|
11
11
|
import { runSyncCommand } from "./commands/sync.js";
|
|
12
|
+
import { runUpgradeCommand } from "./commands/upgrade.js";
|
|
12
13
|
import { runUpdateCommand } from "./commands/update.js";
|
|
13
14
|
import { formatUnknownCommandError, getRootHelpText } from "./core/copy.js";
|
|
14
15
|
import { maybePromptManageAgentsBootstrap } from "./core/manage-agents-bootstrap.js";
|
|
@@ -36,23 +37,24 @@ export async function runCli(argv) {
|
|
|
36
37
|
}
|
|
37
38
|
const parsed = parseArgs(argv);
|
|
38
39
|
const cwd = process.cwd();
|
|
39
|
-
const shouldBootstrapManageAgents = await maybePromptManageAgentsBootstrap({
|
|
40
|
-
command,
|
|
41
|
-
help: Boolean(parsed.help),
|
|
42
|
-
yes: Boolean(parsed.yes),
|
|
43
|
-
cwd,
|
|
44
|
-
});
|
|
45
40
|
const route = parseCommandRoute(argv);
|
|
46
41
|
if (!parsed.help) {
|
|
47
42
|
await maybeNotifyVersionUpdate({
|
|
48
43
|
command,
|
|
44
|
+
argv,
|
|
49
45
|
currentVersion: version,
|
|
50
46
|
});
|
|
51
47
|
}
|
|
52
48
|
if (!route) {
|
|
53
49
|
throw new Error(formatUnknownCommandError(command));
|
|
54
50
|
}
|
|
55
|
-
|
|
51
|
+
const shouldBootstrapManageAgents = await maybePromptManageAgentsBootstrap({
|
|
52
|
+
command,
|
|
53
|
+
help: Boolean(parsed.help),
|
|
54
|
+
yes: Boolean(parsed.yes),
|
|
55
|
+
cwd,
|
|
56
|
+
});
|
|
57
|
+
await runRoutedCommand(route, parsed, cwd, command, version);
|
|
56
58
|
if (shouldBootstrapManageAgents) {
|
|
57
59
|
const bootstrapArgs = buildManageAgentsBootstrapArgs(parsed, cwd);
|
|
58
60
|
await runSkillCommand(parseArgs(bootstrapArgs), cwd);
|
|
@@ -61,7 +63,7 @@ export async function runCli(argv) {
|
|
|
61
63
|
function printHelp() {
|
|
62
64
|
console.log(getRootHelpText());
|
|
63
65
|
}
|
|
64
|
-
async function runRoutedCommand(route, parsed, cwd, command) {
|
|
66
|
+
async function runRoutedCommand(route, parsed, cwd, command, version) {
|
|
65
67
|
if (!route) {
|
|
66
68
|
throw new Error(formatUnknownCommandError(command));
|
|
67
69
|
}
|
|
@@ -76,6 +78,9 @@ async function runRoutedCommand(route, parsed, cwd, command) {
|
|
|
76
78
|
case "update":
|
|
77
79
|
await runUpdateCommand(parsed, cwd);
|
|
78
80
|
return;
|
|
81
|
+
case "upgrade":
|
|
82
|
+
await runUpgradeCommand(parsed, version);
|
|
83
|
+
return;
|
|
79
84
|
case "sync":
|
|
80
85
|
await runSyncCommand(parsed, cwd);
|
|
81
86
|
return;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { getUpgradeHelpText } from "../core/copy.js";
|
|
2
|
+
import { upgradeToLatest } from "../core/version-notifier.js";
|
|
3
|
+
export async function runUpgradeCommand(argv, currentVersion) {
|
|
4
|
+
if (argv.help) {
|
|
5
|
+
console.log(getUpgradeHelpText());
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
const result = await upgradeToLatest({ currentVersion });
|
|
9
|
+
if (result === "updated")
|
|
10
|
+
return;
|
|
11
|
+
if (result === "already-latest") {
|
|
12
|
+
console.log(`agentloom ${currentVersion} is already up to date.`);
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
if (result === "unavailable") {
|
|
16
|
+
throw new Error("Unable to reach npm to check the latest agentloom release.");
|
|
17
|
+
}
|
|
18
|
+
throw new Error("Automatic upgrade failed.");
|
|
19
|
+
}
|
package/dist/core/copy.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export declare function getRootHelpText(): string;
|
|
|
7
7
|
export declare function getFindHelpText(): string;
|
|
8
8
|
export declare function getAddHelpText(): string;
|
|
9
9
|
export declare function getUpdateHelpText(): string;
|
|
10
|
+
export declare function getUpgradeHelpText(): string;
|
|
10
11
|
export declare function getSyncHelpText(): string;
|
|
11
12
|
export declare function getInitHelpText(): string;
|
|
12
13
|
export declare function getCommandHelpText(): string;
|
package/dist/core/copy.js
CHANGED
|
@@ -12,6 +12,7 @@ Aggregate commands:
|
|
|
12
12
|
init Bootstrap canonical files, migrate providers, then sync
|
|
13
13
|
find <query> Search remote + local entities
|
|
14
14
|
update [source] Refresh lockfile-managed imports
|
|
15
|
+
upgrade Install the latest CLI release
|
|
15
16
|
sync Migrate provider configs then generate provider outputs
|
|
16
17
|
delete <source|name> Delete imported entities by source or name
|
|
17
18
|
|
|
@@ -113,6 +114,18 @@ Example:
|
|
|
113
114
|
agentloom update farnoodma/agents --providers codex,cursor
|
|
114
115
|
`;
|
|
115
116
|
}
|
|
117
|
+
export function getUpgradeHelpText() {
|
|
118
|
+
return `Install the latest agentloom release.
|
|
119
|
+
|
|
120
|
+
Usage:
|
|
121
|
+
agentloom upgrade
|
|
122
|
+
|
|
123
|
+
Behavior:
|
|
124
|
+
- Checks npm for the newest published version
|
|
125
|
+
- Upgrades immediately without interactive prompts
|
|
126
|
+
- Reports when the current version is already up to date
|
|
127
|
+
`;
|
|
128
|
+
}
|
|
116
129
|
export function getSyncHelpText() {
|
|
117
130
|
return `Migrate provider configs into canonical .agents data, then generate provider-specific outputs.
|
|
118
131
|
|
package/dist/core/lockfile.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import path from "node:path";
|
|
1
2
|
import { readJsonIfExists, writeJsonAtomic } from "./fs.js";
|
|
2
3
|
const EMPTY_LOCK = {
|
|
3
4
|
version: 1,
|
|
@@ -10,7 +11,7 @@ export function readLockfile(paths) {
|
|
|
10
11
|
}
|
|
11
12
|
return {
|
|
12
13
|
version: 1,
|
|
13
|
-
entries: lock.entries.map((entry) => ({
|
|
14
|
+
entries: lock.entries.map((entry) => normalizeLockEntryForRuntime(paths, {
|
|
14
15
|
...entry,
|
|
15
16
|
importedAgents: Array.isArray(entry.importedAgents)
|
|
16
17
|
? entry.importedAgents
|
|
@@ -66,7 +67,10 @@ function normalizeRenameMap(value) {
|
|
|
66
67
|
return Object.keys(normalized).length > 0 ? normalized : undefined;
|
|
67
68
|
}
|
|
68
69
|
export function writeLockfile(paths, lockfile) {
|
|
69
|
-
writeJsonAtomic(paths.lockPath,
|
|
70
|
+
writeJsonAtomic(paths.lockPath, {
|
|
71
|
+
version: 1,
|
|
72
|
+
entries: lockfile.entries.map((entry) => normalizeLockEntryForDisk(paths, entry)),
|
|
73
|
+
});
|
|
70
74
|
}
|
|
71
75
|
export function upsertLockEntry(lockfile, entry) {
|
|
72
76
|
const index = lockfile.entries.findIndex((item) => item.source === entry.source &&
|
|
@@ -106,3 +110,31 @@ function normalizeSelectionForKey(value) {
|
|
|
106
110
|
...new Set(value.map((item) => item.trim().toLowerCase()).filter(Boolean)),
|
|
107
111
|
].sort();
|
|
108
112
|
}
|
|
113
|
+
function normalizeLockEntryForRuntime(paths, entry) {
|
|
114
|
+
if (paths.scope !== "local" || entry.sourceType !== "local") {
|
|
115
|
+
return entry;
|
|
116
|
+
}
|
|
117
|
+
if (path.isAbsolute(entry.source)) {
|
|
118
|
+
return entry;
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
...entry,
|
|
122
|
+
source: path.resolve(paths.workspaceRoot, entry.source),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function normalizeLockEntryForDisk(paths, entry) {
|
|
126
|
+
if (paths.scope !== "local" || entry.sourceType !== "local") {
|
|
127
|
+
return entry;
|
|
128
|
+
}
|
|
129
|
+
if (!path.isAbsolute(entry.source)) {
|
|
130
|
+
return {
|
|
131
|
+
...entry,
|
|
132
|
+
source: entry.source || ".",
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
const relativeSource = path.relative(paths.workspaceRoot, entry.source);
|
|
136
|
+
return {
|
|
137
|
+
...entry,
|
|
138
|
+
source: relativeSource || ".",
|
|
139
|
+
};
|
|
140
|
+
}
|
package/dist/core/router.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { EntityType } from "../types.js";
|
|
2
|
-
export type AggregateVerb = "add" | "find" | "update" | "sync" | "delete" | "init";
|
|
2
|
+
export type AggregateVerb = "add" | "find" | "update" | "upgrade" | "sync" | "delete" | "init";
|
|
3
3
|
export type EntityVerb = "add" | "list" | "delete" | "find" | "update" | "sync";
|
|
4
4
|
export type McpServerVerb = "add" | "list" | "delete";
|
|
5
5
|
export type CommandRoute = {
|
package/dist/core/router.js
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
type MaybeNotifyOptions = {
|
|
2
2
|
command: string;
|
|
3
|
+
argv: string[];
|
|
3
4
|
packageName?: string;
|
|
4
5
|
currentVersion: string;
|
|
5
6
|
};
|
|
7
|
+
export type UpgradeResult = "updated" | "already-latest" | "failed" | "unavailable";
|
|
6
8
|
export declare function maybeNotifyVersionUpdate(options: MaybeNotifyOptions): Promise<void>;
|
|
9
|
+
export declare function upgradeToLatest(options: {
|
|
10
|
+
currentVersion: string;
|
|
11
|
+
packageName?: string;
|
|
12
|
+
}): Promise<UpgradeResult>;
|
|
7
13
|
export declare function isNewerVersion(candidate: string, current: string): boolean;
|
|
8
|
-
export declare function promptAndUpdate(current: string, latest: string
|
|
14
|
+
export declare function promptAndUpdate(current: string, latest: string, options?: {
|
|
15
|
+
packageName?: string;
|
|
16
|
+
rerunArgs?: string[];
|
|
17
|
+
}): Promise<"updated" | "already-latest" | "failed">;
|
|
18
|
+
export declare function maybeConfirmAutoUpgrade(current: string, latest: string): Promise<boolean>;
|
|
9
19
|
export {};
|
|
@@ -6,50 +6,73 @@ import path from "node:path";
|
|
|
6
6
|
import { confirm, isCancel } from "@clack/prompts";
|
|
7
7
|
import { ensureDir, writeJsonAtomic } from "./fs.js";
|
|
8
8
|
const UPDATE_CACHE_PATH = path.join(os.homedir(), ".agents", ".agentloom-version-cache.json");
|
|
9
|
-
const CHECK_INTERVAL_MS =
|
|
9
|
+
const CHECK_INTERVAL_MS = 2 * 60 * 60 * 1000;
|
|
10
10
|
const REQUEST_TIMEOUT_MS = 1800;
|
|
11
|
+
const DISABLE_UPDATE_ENV = "AGENTLOOM_DISABLE_UPDATE_NOTIFIER";
|
|
12
|
+
const SKIP_COMMANDS = new Set([
|
|
13
|
+
"help",
|
|
14
|
+
"--help",
|
|
15
|
+
"-h",
|
|
16
|
+
"version",
|
|
17
|
+
"--version",
|
|
18
|
+
"-v",
|
|
19
|
+
"upgrade",
|
|
20
|
+
]);
|
|
11
21
|
export async function maybeNotifyVersionUpdate(options) {
|
|
12
|
-
if (process.env
|
|
22
|
+
if (process.env[DISABLE_UPDATE_ENV] === "1")
|
|
13
23
|
return;
|
|
14
|
-
|
|
24
|
+
const loweredCommand = options.command.trim().toLowerCase();
|
|
25
|
+
if (SKIP_COMMANDS.has(loweredCommand))
|
|
15
26
|
return;
|
|
16
|
-
const loweredCommand = options.command.toLowerCase();
|
|
17
|
-
if (loweredCommand === "help" ||
|
|
18
|
-
loweredCommand === "--help" ||
|
|
19
|
-
loweredCommand === "-h" ||
|
|
20
|
-
loweredCommand === "version" ||
|
|
21
|
-
loweredCommand === "--version" ||
|
|
22
|
-
loweredCommand === "-v") {
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
27
|
const packageName = options.packageName ?? "agentloom";
|
|
26
28
|
const cache = readVersionCache();
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
cache
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
const latest = await resolveLatestVersion({
|
|
30
|
+
packageName,
|
|
31
|
+
cache,
|
|
32
|
+
forceFetch: false,
|
|
33
|
+
});
|
|
34
|
+
if (!latest)
|
|
35
|
+
return;
|
|
36
|
+
if (!isNewerVersion(latest, options.currentVersion))
|
|
37
|
+
return;
|
|
34
38
|
const now = Date.now();
|
|
35
|
-
|
|
36
|
-
if (lastChecked && now - lastChecked < CHECK_INTERVAL_MS) {
|
|
39
|
+
if (!shouldAttemptAutoUpgrade(cache, latest, now)) {
|
|
37
40
|
return;
|
|
38
41
|
}
|
|
39
|
-
const
|
|
40
|
-
if (!
|
|
41
|
-
cache
|
|
42
|
+
const approved = await maybeConfirmAutoUpgrade(options.currentVersion, latest);
|
|
43
|
+
if (!approved) {
|
|
44
|
+
recordUpgradeAttempt(cache, latest, now);
|
|
42
45
|
writeVersionCache(cache);
|
|
43
46
|
return;
|
|
44
47
|
}
|
|
45
|
-
|
|
46
|
-
cache
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
// Persist attempt before upgrade/rerun, because rerun success paths can exit the process.
|
|
49
|
+
recordUpgradeAttempt(cache, latest, now);
|
|
50
|
+
writeVersionCache(cache);
|
|
51
|
+
await promptAndUpdate(options.currentVersion, latest, {
|
|
52
|
+
packageName,
|
|
53
|
+
rerunArgs: options.argv,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
export async function upgradeToLatest(options) {
|
|
57
|
+
const packageName = options.packageName ?? "agentloom";
|
|
58
|
+
const cache = readVersionCache();
|
|
59
|
+
const latest = await resolveLatestVersion({
|
|
60
|
+
packageName,
|
|
61
|
+
cache,
|
|
62
|
+
forceFetch: true,
|
|
63
|
+
});
|
|
64
|
+
if (!latest)
|
|
65
|
+
return "unavailable";
|
|
66
|
+
if (!isNewerVersion(latest, options.currentVersion)) {
|
|
67
|
+
return "already-latest";
|
|
51
68
|
}
|
|
69
|
+
const result = await promptAndUpdate(options.currentVersion, latest, {
|
|
70
|
+
packageName,
|
|
71
|
+
});
|
|
72
|
+
const now = Date.now();
|
|
73
|
+
recordUpgradeAttempt(cache, latest, now);
|
|
52
74
|
writeVersionCache(cache);
|
|
75
|
+
return result;
|
|
53
76
|
}
|
|
54
77
|
export function isNewerVersion(candidate, current) {
|
|
55
78
|
const next = parseSemver(candidate);
|
|
@@ -111,24 +134,54 @@ function fetchLatestVersion(packageName) {
|
|
|
111
134
|
req.on("error", () => resolve(null));
|
|
112
135
|
});
|
|
113
136
|
}
|
|
114
|
-
export async function promptAndUpdate(current, latest) {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
137
|
+
export async function promptAndUpdate(current, latest, options = {}) {
|
|
138
|
+
if (!isNewerVersion(latest, current))
|
|
139
|
+
return "already-latest";
|
|
140
|
+
const packageName = options.packageName ?? "agentloom";
|
|
141
|
+
const rerunArgs = options.rerunArgs ?? [];
|
|
142
|
+
if (rerunArgs.length > 0 && isLikelyNpxExecution()) {
|
|
143
|
+
if (!canRunPackageViaNpx(packageName, latest)) {
|
|
144
|
+
console.error("\nAutomatic npx upgrade is currently unavailable; continuing with current version.\n");
|
|
145
|
+
return "failed";
|
|
146
|
+
}
|
|
147
|
+
console.log(`\nUpdate available: ${current} → ${latest}. Re-running with npx ${packageName}@${latest}...\n`);
|
|
148
|
+
const rerun = spawnSync("npx", ["--yes", `${packageName}@${latest}`, ...rerunArgs], {
|
|
149
|
+
stdio: "inherit",
|
|
150
|
+
env: buildChildEnv(),
|
|
151
|
+
});
|
|
152
|
+
if (rerun.error || rerun.status === null) {
|
|
153
|
+
console.error("\nAutomatic npx rerun failed after upgrade. Please run your command again.\n");
|
|
154
|
+
process.exit(1);
|
|
155
|
+
return "failed"; // unreachable, but satisfies type checker
|
|
156
|
+
}
|
|
157
|
+
process.exit(rerun.status);
|
|
158
|
+
return rerun.status === 0 ? "updated" : "failed"; // unreachable, but satisfies type checker
|
|
159
|
+
}
|
|
160
|
+
console.log(`\nUpdating ${packageName} ${current} → ${latest}...\n`);
|
|
161
|
+
const install = spawnSync("npm", ["i", "-g", `${packageName}@${latest}`], {
|
|
162
|
+
stdio: "inherit",
|
|
118
163
|
});
|
|
119
|
-
if (
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
164
|
+
if (install.status !== 0) {
|
|
165
|
+
console.error("\nAutomatic upgrade failed. Run `agentloom upgrade` to retry.\n");
|
|
166
|
+
return "failed";
|
|
167
|
+
}
|
|
168
|
+
if (rerunArgs.length === 0) {
|
|
169
|
+
console.log(`\nUpdated to ${latest}.\n`);
|
|
170
|
+
return "updated";
|
|
171
|
+
}
|
|
172
|
+
console.log(`\nUpdated to ${latest}. Re-running your command...\n`);
|
|
173
|
+
const invocation = buildRerunInvocation(rerunArgs);
|
|
174
|
+
const rerun = spawnSync(invocation.command, invocation.args, {
|
|
123
175
|
stdio: "inherit",
|
|
176
|
+
env: buildChildEnv(),
|
|
124
177
|
});
|
|
125
|
-
if (
|
|
126
|
-
console.
|
|
127
|
-
process.exit(
|
|
128
|
-
return "
|
|
178
|
+
if (rerun.error || rerun.status === null) {
|
|
179
|
+
console.error("\nAutomatic rerun failed after upgrade. Please run your command again.\n");
|
|
180
|
+
process.exit(1);
|
|
181
|
+
return "failed"; // unreachable, but satisfies type checker
|
|
129
182
|
}
|
|
130
|
-
|
|
131
|
-
return "
|
|
183
|
+
process.exit(rerun.status);
|
|
184
|
+
return "updated"; // unreachable, but satisfies type checker
|
|
132
185
|
}
|
|
133
186
|
function readVersionCache() {
|
|
134
187
|
try {
|
|
@@ -158,3 +211,85 @@ function parseTime(value) {
|
|
|
158
211
|
return null;
|
|
159
212
|
return parsed;
|
|
160
213
|
}
|
|
214
|
+
async function resolveLatestVersion(options) {
|
|
215
|
+
const now = Date.now();
|
|
216
|
+
const lastChecked = parseTime(options.cache.lastCheckedAt);
|
|
217
|
+
const shouldFetch = options.forceFetch ||
|
|
218
|
+
!lastChecked ||
|
|
219
|
+
now - lastChecked >= CHECK_INTERVAL_MS;
|
|
220
|
+
if (!shouldFetch) {
|
|
221
|
+
return options.cache.latestVersion ?? null;
|
|
222
|
+
}
|
|
223
|
+
const fetched = await fetchLatestVersion(options.packageName);
|
|
224
|
+
options.cache.lastCheckedAt = new Date(now).toISOString();
|
|
225
|
+
if (fetched) {
|
|
226
|
+
options.cache.latestVersion = fetched;
|
|
227
|
+
}
|
|
228
|
+
writeVersionCache(options.cache);
|
|
229
|
+
if (options.forceFetch) {
|
|
230
|
+
return fetched ?? null;
|
|
231
|
+
}
|
|
232
|
+
return fetched ?? options.cache.latestVersion ?? null;
|
|
233
|
+
}
|
|
234
|
+
function shouldAttemptAutoUpgrade(cache, latest, now) {
|
|
235
|
+
const lastAttemptVersion = cache.lastUpgradeAttemptVersion ?? cache.lastAutoUpgradeVersion;
|
|
236
|
+
if (lastAttemptVersion !== latest)
|
|
237
|
+
return true;
|
|
238
|
+
const lastAttempt = parseTime(cache.lastUpgradeAttemptAt ?? cache.lastAutoUpgradeAt);
|
|
239
|
+
if (!lastAttempt)
|
|
240
|
+
return true;
|
|
241
|
+
return now - lastAttempt >= CHECK_INTERVAL_MS;
|
|
242
|
+
}
|
|
243
|
+
function isLikelyNpxExecution() {
|
|
244
|
+
const npmCommand = process.env.npm_command?.trim().toLowerCase();
|
|
245
|
+
if (npmCommand === "exec")
|
|
246
|
+
return true;
|
|
247
|
+
const argv1 = process.argv[1];
|
|
248
|
+
if (typeof argv1 !== "string" || argv1.length === 0)
|
|
249
|
+
return false;
|
|
250
|
+
return argv1.includes(`${path.sep}_npx${path.sep}`);
|
|
251
|
+
}
|
|
252
|
+
function buildChildEnv() {
|
|
253
|
+
return {
|
|
254
|
+
...process.env,
|
|
255
|
+
[DISABLE_UPDATE_ENV]: "1",
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
export async function maybeConfirmAutoUpgrade(current, latest) {
|
|
259
|
+
if (!isInteractiveTty())
|
|
260
|
+
return true;
|
|
261
|
+
const approved = await confirm({
|
|
262
|
+
message: `Update available: ${current} → ${latest}. Upgrade now and re-run your command?`,
|
|
263
|
+
initialValue: true,
|
|
264
|
+
});
|
|
265
|
+
if (isCancel(approved) || approved !== true)
|
|
266
|
+
return false;
|
|
267
|
+
return true;
|
|
268
|
+
}
|
|
269
|
+
function isInteractiveTty() {
|
|
270
|
+
return Boolean(process.stdin.isTTY && process.stdout.isTTY && process.stderr.isTTY);
|
|
271
|
+
}
|
|
272
|
+
function recordUpgradeAttempt(cache, latest, now) {
|
|
273
|
+
cache.lastUpgradeAttemptVersion = latest;
|
|
274
|
+
cache.lastUpgradeAttemptAt = new Date(now).toISOString();
|
|
275
|
+
}
|
|
276
|
+
function canRunPackageViaNpx(packageName, version) {
|
|
277
|
+
const probe = spawnSync("npx", ["--yes", `${packageName}@${version}`, "--version"], {
|
|
278
|
+
stdio: "ignore",
|
|
279
|
+
env: buildChildEnv(),
|
|
280
|
+
});
|
|
281
|
+
return !probe.error && probe.status === 0;
|
|
282
|
+
}
|
|
283
|
+
function buildRerunInvocation(rerunArgs) {
|
|
284
|
+
const scriptPath = process.argv[1];
|
|
285
|
+
if (typeof scriptPath === "string" && scriptPath.trim().length > 0) {
|
|
286
|
+
return {
|
|
287
|
+
command: process.execPath,
|
|
288
|
+
args: [...process.execArgv, scriptPath, ...rerunArgs],
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
return {
|
|
292
|
+
command: "agentloom",
|
|
293
|
+
args: rerunArgs,
|
|
294
|
+
};
|
|
295
|
+
}
|