agent-dealer 0.1.13 → 0.2.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/bundle/server/package.json +2 -2
- package/bundle/shared/package.json +1 -1
- package/dist/doctor.js +18 -0
- package/dist/index.js +14 -2
- package/dist/install.d.ts +8 -0
- package/dist/install.js +72 -0
- package/dist/managed/activate.d.ts +2 -0
- package/dist/managed/activate.js +49 -0
- package/dist/managed/activate.test.d.ts +1 -0
- package/dist/managed/activate.test.js +34 -0
- package/dist/managed/index.d.ts +8 -0
- package/dist/managed/index.js +8 -0
- package/dist/managed/install-kind.d.ts +5 -0
- package/dist/managed/install-kind.js +16 -0
- package/dist/managed/launcher.d.ts +1 -0
- package/dist/managed/launcher.js +19 -0
- package/dist/managed/npm-prefix-install.d.ts +16 -0
- package/dist/managed/npm-prefix-install.js +38 -0
- package/dist/managed/paths.d.ts +11 -0
- package/dist/managed/paths.js +45 -0
- package/dist/managed/semver.d.ts +2 -0
- package/dist/managed/semver.js +12 -0
- package/dist/managed/update-state.d.ts +7 -0
- package/dist/managed/update-state.js +22 -0
- package/dist/managed/updater.d.ts +21 -0
- package/dist/managed/updater.js +107 -0
- package/dist/update-check.d.ts +1 -0
- package/dist/update-check.js +25 -37
- package/dist/upgrade.d.ts +1 -0
- package/dist/upgrade.js +55 -6
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-dealer/server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"files": [
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"typecheck": "tsc --noEmit"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@agent-dealer/shared": "
|
|
24
|
+
"@agent-dealer/shared": "0.2.0",
|
|
25
25
|
"@fastify/cors": "^11.0.1",
|
|
26
26
|
"@fastify/static": "^8.2.0",
|
|
27
27
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
package/dist/doctor.js
CHANGED
|
@@ -4,6 +4,8 @@ import { claudeAvailable } from "./cli-check.js";
|
|
|
4
4
|
import { loadProdEnvFile, prodEnvFilePath, prodHomeDir, resolveBundledListenPort, shortenHome, } from "./env.js";
|
|
5
5
|
import { probeAgentDealer } from "./ports.js";
|
|
6
6
|
import { resolveServerEntry, resolveUiDist } from "./paths.js";
|
|
7
|
+
import { detectInstallKind, localBinLauncherPath, readCurrentManagedVersion, readUpdateState, resolveCurrentVersionDir, } from "./managed/index.js";
|
|
8
|
+
import { getVersion } from "./version.js";
|
|
7
9
|
export async function runDoctor() {
|
|
8
10
|
let failed = false;
|
|
9
11
|
const major = Number(process.versions.node.split(".")[0]);
|
|
@@ -53,6 +55,22 @@ export async function runDoctor() {
|
|
|
53
55
|
if (fs.existsSync(home)) {
|
|
54
56
|
console.log(`✓ data ${shortenHome(home)}`);
|
|
55
57
|
}
|
|
58
|
+
console.log(`Package version ${getVersion()}`);
|
|
59
|
+
const kind = detectInstallKind();
|
|
60
|
+
console.log(`Install kind: ${kind}`);
|
|
61
|
+
if (kind === "managed") {
|
|
62
|
+
const current = readCurrentManagedVersion();
|
|
63
|
+
const currentDir = resolveCurrentVersionDir();
|
|
64
|
+
console.log(`Managed current: ${current ?? "(unknown)"} (${currentDir ?? "missing"})`);
|
|
65
|
+
console.log(`Launcher: ${localBinLauncherPath()}`);
|
|
66
|
+
const pending = readUpdateState()?.pendingVersion;
|
|
67
|
+
if (pending) {
|
|
68
|
+
console.log(`Pending managed version: ${pending} (activates on next start/doctor/upgrade)`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
console.log("Tip: agent-dealer install # managed CLI + auto-updates (data unchanged)");
|
|
73
|
+
}
|
|
56
74
|
const port = resolveBundledListenPort();
|
|
57
75
|
const probe = await probeAgentDealer("127.0.0.1", port);
|
|
58
76
|
if (probe.up) {
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import { runStop } from "./stop.js";
|
|
|
6
6
|
import { maybeCheckForUpdateOnRun } from "./update-check.js";
|
|
7
7
|
import { getVersion } from "./version.js";
|
|
8
8
|
import { runUpgrade, printUpgradeHelp } from "./upgrade.js";
|
|
9
|
+
import { runInstall, printInstallHelp } from "./install.js";
|
|
9
10
|
function printUsage() {
|
|
10
11
|
console.log(`Usage:
|
|
11
12
|
agent-dealer setup [--home DIR] [--force]
|
|
@@ -13,7 +14,8 @@ function printUsage() {
|
|
|
13
14
|
agent-dealer stop
|
|
14
15
|
agent-dealer status
|
|
15
16
|
agent-dealer doctor
|
|
16
|
-
agent-dealer
|
|
17
|
+
agent-dealer install [--to VERSION] [--migrate-cli] [--purge-global]
|
|
18
|
+
agent-dealer upgrade [--to VERSION] [--yes] [--check]
|
|
17
19
|
agent-dealer --version
|
|
18
20
|
|
|
19
21
|
Human control plane for agent execution.`);
|
|
@@ -92,6 +94,13 @@ export async function runCli(argv) {
|
|
|
92
94
|
return 0;
|
|
93
95
|
return runDoctor();
|
|
94
96
|
}
|
|
97
|
+
if (cmd === "install") {
|
|
98
|
+
if (args.includes("--help")) {
|
|
99
|
+
printInstallHelp();
|
|
100
|
+
return 0;
|
|
101
|
+
}
|
|
102
|
+
return runInstall(args.slice(1));
|
|
103
|
+
}
|
|
95
104
|
if (cmd === "upgrade") {
|
|
96
105
|
if (args.includes("--help")) {
|
|
97
106
|
printUpgradeHelp();
|
|
@@ -99,17 +108,20 @@ export async function runCli(argv) {
|
|
|
99
108
|
}
|
|
100
109
|
let toVersion;
|
|
101
110
|
let yes = false;
|
|
111
|
+
let check = false;
|
|
102
112
|
for (let i = 1; i < args.length; i += 1) {
|
|
103
113
|
if (args[i] === "--to")
|
|
104
114
|
toVersion = args[++i];
|
|
105
115
|
else if (args[i] === "--yes")
|
|
106
116
|
yes = true;
|
|
117
|
+
else if (args[i] === "--check")
|
|
118
|
+
check = true;
|
|
107
119
|
else {
|
|
108
120
|
console.error(`Unknown upgrade option: ${args[i]}`);
|
|
109
121
|
return 1;
|
|
110
122
|
}
|
|
111
123
|
}
|
|
112
|
-
return runUpgrade({ toVersion, yes });
|
|
124
|
+
return runUpgrade({ toVersion, yes, check });
|
|
113
125
|
}
|
|
114
126
|
console.error(`Unknown command: ${cmd}`);
|
|
115
127
|
printUsage();
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { installCliVersionToPrefix } from "./managed/index.js";
|
|
2
|
+
export type InstallDeps = {
|
|
3
|
+
installVersion?: typeof installCliVersionToPrefix;
|
|
4
|
+
fetchLatest?: () => Promise<string | null>;
|
|
5
|
+
purgeGlobal?: () => Promise<number>;
|
|
6
|
+
};
|
|
7
|
+
export declare function printInstallHelp(): void;
|
|
8
|
+
export declare function runInstall(args: string[], deps?: InstallDeps): Promise<number>;
|
package/dist/install.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { activateVersion, cliEntryInVersionDir, fetchLatestVersion, installCliVersionToPrefix, localBinLauncherPath, PACKAGE_NAME, versionDir, } from "./managed/index.js";
|
|
6
|
+
function parseArgs(args) {
|
|
7
|
+
let to;
|
|
8
|
+
let migrateCli = false;
|
|
9
|
+
let purgeGlobal = false;
|
|
10
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
11
|
+
const a = args[i];
|
|
12
|
+
if (a === "--to")
|
|
13
|
+
to = args[++i];
|
|
14
|
+
else if (a === "--migrate-cli")
|
|
15
|
+
migrateCli = true;
|
|
16
|
+
else if (a === "--purge-global")
|
|
17
|
+
purgeGlobal = true;
|
|
18
|
+
}
|
|
19
|
+
return { to, migrateCli, purgeGlobal };
|
|
20
|
+
}
|
|
21
|
+
export function printInstallHelp() {
|
|
22
|
+
console.log(`Usage:
|
|
23
|
+
agent-dealer install [--to VERSION] [--migrate-cli] [--purge-global]
|
|
24
|
+
|
|
25
|
+
Installs agent-dealer into ~/.agent-dealer/versions and writes ~/.local/bin/agent-dealer.
|
|
26
|
+
Existing config, queue data, and logs are left unchanged.`);
|
|
27
|
+
}
|
|
28
|
+
export async function runInstall(args, deps = {}) {
|
|
29
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
30
|
+
printInstallHelp();
|
|
31
|
+
return 0;
|
|
32
|
+
}
|
|
33
|
+
const { to, migrateCli, purgeGlobal } = parseArgs(args);
|
|
34
|
+
const fetchLatest = deps.fetchLatest ?? fetchLatestVersion;
|
|
35
|
+
const installVersion = deps.installVersion ?? installCliVersionToPrefix;
|
|
36
|
+
const version = to ?? (await fetchLatest());
|
|
37
|
+
if (!version) {
|
|
38
|
+
console.error("Could not resolve latest version from npm.");
|
|
39
|
+
return 1;
|
|
40
|
+
}
|
|
41
|
+
if (!fs.existsSync(cliEntryInVersionDir(versionDir(version)))) {
|
|
42
|
+
const result = await installVersion(version);
|
|
43
|
+
if (!result.ok) {
|
|
44
|
+
console.error(`Managed install failed: ${result.error}`);
|
|
45
|
+
return 1;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
activateVersion(version);
|
|
49
|
+
const home = process.env.AGENT_DEALER_HOME?.trim() || path.join(os.homedir(), ".agent-dealer");
|
|
50
|
+
console.log(`Installed ${PACKAGE_NAME}@${version} to ${home} (data home unchanged).`);
|
|
51
|
+
console.log(`Launcher: ${localBinLauncherPath()}`);
|
|
52
|
+
console.log('If command not found: export PATH="$HOME/.local/bin:$PATH"');
|
|
53
|
+
if (migrateCli) {
|
|
54
|
+
console.log("Prefer ~/.local/bin ahead of any npm global agent-dealer on PATH.");
|
|
55
|
+
}
|
|
56
|
+
console.log("Next: agent-dealer doctor && agent-dealer start --daemon --open");
|
|
57
|
+
if (purgeGlobal) {
|
|
58
|
+
const code = deps.purgeGlobal
|
|
59
|
+
? await deps.purgeGlobal()
|
|
60
|
+
: await new Promise((resolve) => {
|
|
61
|
+
const child = spawn("npm", ["uninstall", "-g", PACKAGE_NAME], {
|
|
62
|
+
stdio: "inherit",
|
|
63
|
+
shell: process.platform === "win32",
|
|
64
|
+
});
|
|
65
|
+
child.on("exit", (c) => resolve(c ?? 1));
|
|
66
|
+
});
|
|
67
|
+
if (code !== 0) {
|
|
68
|
+
console.warn("Warning: could not uninstall npm global package (managed install is still active).");
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return 0;
|
|
72
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { writeLocalBinLauncher } from "./launcher.js";
|
|
4
|
+
import { cliEntryInVersionDir, currentLinkPath, resolveCurrentVersionDir, versionDir, versionsDir, } from "./paths.js";
|
|
5
|
+
import { compareSemver } from "./semver.js";
|
|
6
|
+
export function activateVersion(version) {
|
|
7
|
+
const target = versionDir(version);
|
|
8
|
+
const entry = cliEntryInVersionDir(target);
|
|
9
|
+
if (!fs.existsSync(entry)) {
|
|
10
|
+
throw new Error(`Cannot activate ${version}: missing ${entry}`);
|
|
11
|
+
}
|
|
12
|
+
const link = currentLinkPath();
|
|
13
|
+
fs.mkdirSync(path.dirname(link), { recursive: true });
|
|
14
|
+
try {
|
|
15
|
+
const st = fs.lstatSync(link);
|
|
16
|
+
if (st.isSymbolicLink() || st.isFile()) {
|
|
17
|
+
fs.unlinkSync(link);
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
fs.rmSync(link, { recursive: true, force: true });
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
// missing
|
|
25
|
+
}
|
|
26
|
+
fs.symlinkSync(target, link, "dir");
|
|
27
|
+
writeLocalBinLauncher();
|
|
28
|
+
pruneOldVersions(3);
|
|
29
|
+
}
|
|
30
|
+
export function pruneOldVersions(keep) {
|
|
31
|
+
const root = versionsDir();
|
|
32
|
+
if (!fs.existsSync(root))
|
|
33
|
+
return;
|
|
34
|
+
const currentReal = resolveCurrentVersionDir();
|
|
35
|
+
const names = fs
|
|
36
|
+
.readdirSync(root, { withFileTypes: true })
|
|
37
|
+
.filter((d) => d.isDirectory() && !d.name.startsWith("."))
|
|
38
|
+
.map((d) => d.name)
|
|
39
|
+
.sort((a, b) => compareSemver(b, a));
|
|
40
|
+
const retained = names.slice(0, Math.max(keep, 0));
|
|
41
|
+
for (const name of names) {
|
|
42
|
+
const full = path.join(root, name);
|
|
43
|
+
if (retained.includes(name))
|
|
44
|
+
continue;
|
|
45
|
+
if (currentReal && fs.existsSync(full) && fs.realpathSync(full) === currentReal)
|
|
46
|
+
continue;
|
|
47
|
+
fs.rmSync(full, { recursive: true, force: true });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import test from "node:test";
|
|
6
|
+
import { activateVersion } from "./activate.js";
|
|
7
|
+
import { detectInstallKind } from "./install-kind.js";
|
|
8
|
+
import { currentLinkPath, localBinLauncherPath, versionDir } from "./paths.js";
|
|
9
|
+
function seedVersion(ver) {
|
|
10
|
+
const dir = versionDir(ver);
|
|
11
|
+
const bin = path.join(dir, "node_modules", "agent-dealer", "dist", "bin.js");
|
|
12
|
+
fs.mkdirSync(path.dirname(bin), { recursive: true });
|
|
13
|
+
fs.writeFileSync(bin, "ok\n");
|
|
14
|
+
}
|
|
15
|
+
test("managed activate preserves sibling data and writes launcher", () => {
|
|
16
|
+
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "adlr-act-"));
|
|
17
|
+
const localBin = path.join(tmp, "local-bin");
|
|
18
|
+
process.env.AGENT_DEALER_HOME = tmp;
|
|
19
|
+
process.env.AGENT_DEALER_LOCAL_BIN = localBin;
|
|
20
|
+
try {
|
|
21
|
+
fs.writeFileSync(path.join(tmp, ".env"), "KEEP=1\n");
|
|
22
|
+
seedVersion("0.1.0");
|
|
23
|
+
activateVersion("0.1.0");
|
|
24
|
+
assert.equal(fs.realpathSync(currentLinkPath()), fs.realpathSync(versionDir("0.1.0")));
|
|
25
|
+
assert.equal(fs.readFileSync(path.join(tmp, ".env"), "utf8"), "KEEP=1\n");
|
|
26
|
+
assert.ok(fs.existsSync(localBinLauncherPath()));
|
|
27
|
+
assert.equal(detectInstallKind(), "managed");
|
|
28
|
+
}
|
|
29
|
+
finally {
|
|
30
|
+
delete process.env.AGENT_DEALER_HOME;
|
|
31
|
+
delete process.env.AGENT_DEALER_LOCAL_BIN;
|
|
32
|
+
fs.rmSync(tmp, { recursive: true, force: true });
|
|
33
|
+
}
|
|
34
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { agentDealerHome, cliEntryInVersionDir, currentLinkPath, localBinDir, localBinLauncherPath, partialVersionDir, resolveCurrentVersionDir, updateStatePath, versionDir, versionsDir, } from "./paths.js";
|
|
2
|
+
export { detectInstallKind, type InstallKind } from "./install-kind.js";
|
|
3
|
+
export { activateVersion, pruneOldVersions } from "./activate.js";
|
|
4
|
+
export { writeLocalBinLauncher } from "./launcher.js";
|
|
5
|
+
export { installCliVersionToPrefix, PACKAGE_NAME } from "./npm-prefix-install.js";
|
|
6
|
+
export { compareSemver } from "./semver.js";
|
|
7
|
+
export { readUpdateState, writeUpdateState, type UpdateState } from "./update-state.js";
|
|
8
|
+
export { ensurePendingDownload, fetchLatestVersion, isAutoupdaterDisabled, maybeActivatePendingVersion, readCurrentManagedVersion, runManagedCliEntryHooks, scheduleBackgroundUpdateCheck, } from "./updater.js";
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { agentDealerHome, cliEntryInVersionDir, currentLinkPath, localBinDir, localBinLauncherPath, partialVersionDir, resolveCurrentVersionDir, updateStatePath, versionDir, versionsDir, } from "./paths.js";
|
|
2
|
+
export { detectInstallKind } from "./install-kind.js";
|
|
3
|
+
export { activateVersion, pruneOldVersions } from "./activate.js";
|
|
4
|
+
export { writeLocalBinLauncher } from "./launcher.js";
|
|
5
|
+
export { installCliVersionToPrefix, PACKAGE_NAME } from "./npm-prefix-install.js";
|
|
6
|
+
export { compareSemver } from "./semver.js";
|
|
7
|
+
export { readUpdateState, writeUpdateState } from "./update-state.js";
|
|
8
|
+
export { ensurePendingDownload, fetchLatestVersion, isAutoupdaterDisabled, maybeActivatePendingVersion, readCurrentManagedVersion, runManagedCliEntryHooks, scheduleBackgroundUpdateCheck, } from "./updater.js";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { resolveCurrentVersionDir } from "./paths.js";
|
|
2
|
+
export function detectInstallKind(options) {
|
|
3
|
+
if (resolveCurrentVersionDir())
|
|
4
|
+
return "managed";
|
|
5
|
+
const whichPath = options?.whichPath;
|
|
6
|
+
const prefix = options?.npmGlobalPrefix;
|
|
7
|
+
if (whichPath && prefix) {
|
|
8
|
+
const normalizedWhich = whichPath.replace(/\\/g, "/");
|
|
9
|
+
const normalizedPrefix = prefix.replace(/\\/g, "/").replace(/\/$/, "");
|
|
10
|
+
if (normalizedWhich === normalizedPrefix ||
|
|
11
|
+
normalizedWhich.startsWith(`${normalizedPrefix}/`)) {
|
|
12
|
+
return "npm-global";
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return "unknown";
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function writeLocalBinLauncher(): void;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import { localBinDir, localBinLauncherPath } from "./paths.js";
|
|
3
|
+
const LAUNCHER_BODY = `#!/usr/bin/env bash
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
HOME_DIR="\${AGENT_DEALER_HOME:-$HOME/.agent-dealer}"
|
|
6
|
+
CURRENT="$HOME_DIR/current"
|
|
7
|
+
BIN="$CURRENT/node_modules/agent-dealer/dist/bin.js"
|
|
8
|
+
if [ ! -f "$BIN" ]; then
|
|
9
|
+
echo "agent-dealer: managed install broken (missing $BIN). Re-run: agent-dealer install" >&2
|
|
10
|
+
exit 1
|
|
11
|
+
fi
|
|
12
|
+
exec node "$BIN" "$@"
|
|
13
|
+
`;
|
|
14
|
+
export function writeLocalBinLauncher() {
|
|
15
|
+
fs.mkdirSync(localBinDir(), { recursive: true });
|
|
16
|
+
const target = localBinLauncherPath();
|
|
17
|
+
fs.writeFileSync(target, LAUNCHER_BODY, { mode: 0o755 });
|
|
18
|
+
fs.chmodSync(target, 0o755);
|
|
19
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export declare const PACKAGE_NAME = "agent-dealer";
|
|
2
|
+
export type NpmSpawn = (args: string[], options: {
|
|
3
|
+
cwd?: string;
|
|
4
|
+
}) => Promise<{
|
|
5
|
+
code: number;
|
|
6
|
+
stderr: string;
|
|
7
|
+
}>;
|
|
8
|
+
export declare function installCliVersionToPrefix(version: string, options?: {
|
|
9
|
+
npmSpawn?: NpmSpawn;
|
|
10
|
+
}): Promise<{
|
|
11
|
+
ok: true;
|
|
12
|
+
dir: string;
|
|
13
|
+
} | {
|
|
14
|
+
ok: false;
|
|
15
|
+
error: string;
|
|
16
|
+
}>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import { cliEntryInVersionDir, partialVersionDir, versionDir, versionsDir } from "./paths.js";
|
|
4
|
+
export const PACKAGE_NAME = "agent-dealer";
|
|
5
|
+
const defaultNpmSpawn = (args, options) => new Promise((resolve) => {
|
|
6
|
+
const child = spawn("npm", args, {
|
|
7
|
+
cwd: options.cwd,
|
|
8
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
9
|
+
shell: process.platform === "win32",
|
|
10
|
+
});
|
|
11
|
+
let stderr = "";
|
|
12
|
+
child.stderr?.on("data", (chunk) => {
|
|
13
|
+
stderr += chunk.toString();
|
|
14
|
+
});
|
|
15
|
+
child.on("error", (err) => resolve({ code: 1, stderr: err.message }));
|
|
16
|
+
child.on("exit", (code) => resolve({ code: code ?? 1, stderr }));
|
|
17
|
+
});
|
|
18
|
+
export async function installCliVersionToPrefix(version, options = {}) {
|
|
19
|
+
const npmSpawn = options.npmSpawn ?? defaultNpmSpawn;
|
|
20
|
+
const partial = partialVersionDir(version);
|
|
21
|
+
const finalDir = versionDir(version);
|
|
22
|
+
fs.mkdirSync(versionsDir(), { recursive: true });
|
|
23
|
+
fs.rmSync(partial, { recursive: true, force: true });
|
|
24
|
+
fs.mkdirSync(partial, { recursive: true });
|
|
25
|
+
const result = await npmSpawn(["install", "--prefix", partial, `${PACKAGE_NAME}@${version}`], {});
|
|
26
|
+
if (result.code !== 0) {
|
|
27
|
+
fs.rmSync(partial, { recursive: true, force: true });
|
|
28
|
+
return { ok: false, error: result.stderr.trim() || `npm install failed (exit ${result.code})` };
|
|
29
|
+
}
|
|
30
|
+
const entry = cliEntryInVersionDir(partial);
|
|
31
|
+
if (!fs.existsSync(entry)) {
|
|
32
|
+
fs.rmSync(partial, { recursive: true, force: true });
|
|
33
|
+
return { ok: false, error: `Install succeeded but missing CLI entry: ${entry}` };
|
|
34
|
+
}
|
|
35
|
+
fs.rmSync(finalDir, { recursive: true, force: true });
|
|
36
|
+
fs.renameSync(partial, finalDir);
|
|
37
|
+
return { ok: true, dir: finalDir };
|
|
38
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare function agentDealerHome(): string;
|
|
2
|
+
export declare function versionsDir(): string;
|
|
3
|
+
export declare function versionDir(version: string): string;
|
|
4
|
+
export declare function partialVersionDir(version: string): string;
|
|
5
|
+
export declare function currentLinkPath(): string;
|
|
6
|
+
export declare function updateStatePath(): string;
|
|
7
|
+
/** Override with AGENT_DEALER_LOCAL_BIN for tests. */
|
|
8
|
+
export declare function localBinDir(): string;
|
|
9
|
+
export declare function localBinLauncherPath(): string;
|
|
10
|
+
export declare function cliEntryInVersionDir(dir: string): string;
|
|
11
|
+
export declare function resolveCurrentVersionDir(): string | null;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
export function agentDealerHome() {
|
|
5
|
+
return process.env.AGENT_DEALER_HOME?.trim() || path.join(os.homedir(), ".agent-dealer");
|
|
6
|
+
}
|
|
7
|
+
export function versionsDir() {
|
|
8
|
+
return path.join(agentDealerHome(), "versions");
|
|
9
|
+
}
|
|
10
|
+
export function versionDir(version) {
|
|
11
|
+
return path.join(versionsDir(), version);
|
|
12
|
+
}
|
|
13
|
+
export function partialVersionDir(version) {
|
|
14
|
+
return path.join(versionsDir(), `.partial-${version}`);
|
|
15
|
+
}
|
|
16
|
+
export function currentLinkPath() {
|
|
17
|
+
return path.join(agentDealerHome(), "current");
|
|
18
|
+
}
|
|
19
|
+
export function updateStatePath() {
|
|
20
|
+
return path.join(agentDealerHome(), "update-state.json");
|
|
21
|
+
}
|
|
22
|
+
/** Override with AGENT_DEALER_LOCAL_BIN for tests. */
|
|
23
|
+
export function localBinDir() {
|
|
24
|
+
const override = process.env.AGENT_DEALER_LOCAL_BIN?.trim();
|
|
25
|
+
if (override)
|
|
26
|
+
return override;
|
|
27
|
+
return path.join(os.homedir(), ".local", "bin");
|
|
28
|
+
}
|
|
29
|
+
export function localBinLauncherPath() {
|
|
30
|
+
return path.join(localBinDir(), "agent-dealer");
|
|
31
|
+
}
|
|
32
|
+
export function cliEntryInVersionDir(dir) {
|
|
33
|
+
return path.join(dir, "node_modules", "agent-dealer", "dist", "bin.js");
|
|
34
|
+
}
|
|
35
|
+
export function resolveCurrentVersionDir() {
|
|
36
|
+
const link = currentLinkPath();
|
|
37
|
+
try {
|
|
38
|
+
if (!fs.existsSync(link))
|
|
39
|
+
return null;
|
|
40
|
+
return fs.realpathSync(link);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Semver compare: positive if a > b. */
|
|
2
|
+
export function compareSemver(a, b) {
|
|
3
|
+
const parse = (value) => value.replace(/^v/, "").split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
4
|
+
const left = parse(a);
|
|
5
|
+
const right = parse(b);
|
|
6
|
+
for (let i = 0; i < Math.max(left.length, right.length); i += 1) {
|
|
7
|
+
const diff = (left[i] ?? 0) - (right[i] ?? 0);
|
|
8
|
+
if (diff !== 0)
|
|
9
|
+
return diff > 0 ? 1 : -1;
|
|
10
|
+
}
|
|
11
|
+
return 0;
|
|
12
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { updateStatePath } from "./paths.js";
|
|
4
|
+
export function readUpdateState() {
|
|
5
|
+
try {
|
|
6
|
+
const raw = fs.readFileSync(updateStatePath(), "utf8");
|
|
7
|
+
const parsed = JSON.parse(raw);
|
|
8
|
+
return {
|
|
9
|
+
checkedAt: parsed.checkedAt,
|
|
10
|
+
latest: parsed.latest ?? null,
|
|
11
|
+
pendingVersion: parsed.pendingVersion ?? null,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export function writeUpdateState(state) {
|
|
19
|
+
const file = updateStatePath();
|
|
20
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
21
|
+
fs.writeFileSync(file, `${JSON.stringify(state, null, 2)}\n`, "utf8");
|
|
22
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { installCliVersionToPrefix } from "./npm-prefix-install.js";
|
|
2
|
+
export declare function isAutoupdaterDisabled(): boolean;
|
|
3
|
+
export declare function fetchLatestVersion(): Promise<string | null>;
|
|
4
|
+
export declare function readCurrentManagedVersion(): string | null;
|
|
5
|
+
export declare function maybeActivatePendingVersion(): {
|
|
6
|
+
activated: string | null;
|
|
7
|
+
};
|
|
8
|
+
export declare function ensurePendingDownload(latest: string, options?: {
|
|
9
|
+
installVersion?: typeof installCliVersionToPrefix;
|
|
10
|
+
}): Promise<void>;
|
|
11
|
+
export declare function scheduleBackgroundUpdateCheck(options?: {
|
|
12
|
+
fetchLatest?: () => Promise<string | null>;
|
|
13
|
+
installVersion?: typeof installCliVersionToPrefix;
|
|
14
|
+
}): void;
|
|
15
|
+
export declare function runManagedCliEntryHooks(options: {
|
|
16
|
+
allowActivate: boolean;
|
|
17
|
+
fetchLatest?: () => Promise<string | null>;
|
|
18
|
+
installVersion?: typeof installCliVersionToPrefix;
|
|
19
|
+
}): {
|
|
20
|
+
activated: string | null;
|
|
21
|
+
};
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { activateVersion } from "./activate.js";
|
|
4
|
+
import { detectInstallKind } from "./install-kind.js";
|
|
5
|
+
import { installCliVersionToPrefix } from "./npm-prefix-install.js";
|
|
6
|
+
import { cliEntryInVersionDir, resolveCurrentVersionDir, versionDir } from "./paths.js";
|
|
7
|
+
import { compareSemver } from "./semver.js";
|
|
8
|
+
import { readUpdateState, writeUpdateState } from "./update-state.js";
|
|
9
|
+
import { fetchLatestPublishedVersion } from "../npm-registry.js";
|
|
10
|
+
const CACHE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
11
|
+
const PACKAGE_NAME = "agent-dealer";
|
|
12
|
+
export function isAutoupdaterDisabled() {
|
|
13
|
+
const v = process.env.AGENT_DEALER_DISABLE_AUTOUPDATER?.trim().toLowerCase();
|
|
14
|
+
return v === "1" || v === "true";
|
|
15
|
+
}
|
|
16
|
+
export async function fetchLatestVersion() {
|
|
17
|
+
const v = await fetchLatestPublishedVersion(PACKAGE_NAME);
|
|
18
|
+
return v ?? null;
|
|
19
|
+
}
|
|
20
|
+
export function readCurrentManagedVersion() {
|
|
21
|
+
const real = resolveCurrentVersionDir();
|
|
22
|
+
if (!real)
|
|
23
|
+
return null;
|
|
24
|
+
return path.basename(real);
|
|
25
|
+
}
|
|
26
|
+
export function maybeActivatePendingVersion() {
|
|
27
|
+
if (isAutoupdaterDisabled())
|
|
28
|
+
return { activated: null };
|
|
29
|
+
if (detectInstallKind() !== "managed")
|
|
30
|
+
return { activated: null };
|
|
31
|
+
const state = readUpdateState();
|
|
32
|
+
const pending = state?.pendingVersion;
|
|
33
|
+
if (!pending)
|
|
34
|
+
return { activated: null };
|
|
35
|
+
const dir = versionDir(pending);
|
|
36
|
+
if (!fs.existsSync(cliEntryInVersionDir(dir)))
|
|
37
|
+
return { activated: null };
|
|
38
|
+
activateVersion(pending);
|
|
39
|
+
writeUpdateState({
|
|
40
|
+
checkedAt: state?.checkedAt ?? new Date().toISOString(),
|
|
41
|
+
latest: state?.latest ?? pending,
|
|
42
|
+
pendingVersion: null,
|
|
43
|
+
});
|
|
44
|
+
return { activated: pending };
|
|
45
|
+
}
|
|
46
|
+
export async function ensurePendingDownload(latest, options = {}) {
|
|
47
|
+
if (isAutoupdaterDisabled())
|
|
48
|
+
return;
|
|
49
|
+
const dir = versionDir(latest);
|
|
50
|
+
if (fs.existsSync(cliEntryInVersionDir(dir))) {
|
|
51
|
+
const prev = readUpdateState();
|
|
52
|
+
writeUpdateState({
|
|
53
|
+
checkedAt: prev?.checkedAt ?? new Date().toISOString(),
|
|
54
|
+
latest,
|
|
55
|
+
pendingVersion: latest,
|
|
56
|
+
});
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const install = options.installVersion ?? installCliVersionToPrefix;
|
|
60
|
+
const result = await install(latest);
|
|
61
|
+
if (!result.ok)
|
|
62
|
+
return;
|
|
63
|
+
const prev = readUpdateState();
|
|
64
|
+
writeUpdateState({
|
|
65
|
+
checkedAt: prev?.checkedAt ?? new Date().toISOString(),
|
|
66
|
+
latest,
|
|
67
|
+
pendingVersion: latest,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
export function scheduleBackgroundUpdateCheck(options = {}) {
|
|
71
|
+
if (isAutoupdaterDisabled() || detectInstallKind() !== "managed")
|
|
72
|
+
return;
|
|
73
|
+
const state = readUpdateState();
|
|
74
|
+
if (state?.checkedAt) {
|
|
75
|
+
const age = Date.now() - Date.parse(state.checkedAt);
|
|
76
|
+
if (Number.isFinite(age) && age >= 0 && age < CACHE_TTL_MS)
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const fetchLatest = options.fetchLatest ?? fetchLatestVersion;
|
|
80
|
+
void (async () => {
|
|
81
|
+
try {
|
|
82
|
+
const latest = await fetchLatest();
|
|
83
|
+
writeUpdateState({
|
|
84
|
+
checkedAt: new Date().toISOString(),
|
|
85
|
+
latest,
|
|
86
|
+
pendingVersion: readUpdateState()?.pendingVersion ?? null,
|
|
87
|
+
});
|
|
88
|
+
if (!latest)
|
|
89
|
+
return;
|
|
90
|
+
const current = readCurrentManagedVersion();
|
|
91
|
+
if (current && compareSemver(latest, current) <= 0)
|
|
92
|
+
return;
|
|
93
|
+
await ensurePendingDownload(latest, { installVersion: options.installVersion });
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
// background only
|
|
97
|
+
}
|
|
98
|
+
})();
|
|
99
|
+
}
|
|
100
|
+
export function runManagedCliEntryHooks(options) {
|
|
101
|
+
const activated = options.allowActivate ? maybeActivatePendingVersion().activated : null;
|
|
102
|
+
scheduleBackgroundUpdateCheck({
|
|
103
|
+
fetchLatest: options.fetchLatest,
|
|
104
|
+
installVersion: options.installVersion,
|
|
105
|
+
});
|
|
106
|
+
return { activated };
|
|
107
|
+
}
|
package/dist/update-check.d.ts
CHANGED
package/dist/update-check.js
CHANGED
|
@@ -1,32 +1,15 @@
|
|
|
1
|
+
import { detectInstallKind, runManagedCliEntryHooks } from "./managed/index.js";
|
|
2
|
+
import { installAgentDealerVersion } from "./upgrade.js";
|
|
3
|
+
import { fetchLatestPublishedVersion } from "./npm-registry.js";
|
|
4
|
+
import { getVersion } from "./version.js";
|
|
5
|
+
import { compareSemver } from "./managed/semver.js";
|
|
1
6
|
import fs from "node:fs";
|
|
2
7
|
import path from "node:path";
|
|
3
8
|
import readline from "node:readline";
|
|
4
9
|
import { prodHomeDir } from "./env.js";
|
|
5
|
-
import { getVersion } from "./version.js";
|
|
6
|
-
import { fetchLatestPublishedVersion } from "./npm-registry.js";
|
|
7
|
-
import { installAgentDealerVersion } from "./upgrade.js";
|
|
8
10
|
const PKG_NAME = "agent-dealer";
|
|
9
11
|
const CACHE_FILE = ".agent-dealer-update-check.json";
|
|
10
12
|
const CHECK_THROTTLE_MS = 24 * 60 * 60 * 1000;
|
|
11
|
-
function parseXyzVersion(v) {
|
|
12
|
-
const m = v.trim().match(/^(\d+)\.(\d+)\.(\d+)/);
|
|
13
|
-
if (!m)
|
|
14
|
-
return undefined;
|
|
15
|
-
return { major: Number(m[1]), minor: Number(m[2]), patch: Number(m[3]) };
|
|
16
|
-
}
|
|
17
|
-
function isNewer(latest, current) {
|
|
18
|
-
const a = parseXyzVersion(latest);
|
|
19
|
-
const b = parseXyzVersion(current);
|
|
20
|
-
if (!a || !b)
|
|
21
|
-
return false;
|
|
22
|
-
if (a.major !== b.major)
|
|
23
|
-
return a.major > b.major;
|
|
24
|
-
if (a.minor !== b.minor)
|
|
25
|
-
return a.minor > b.minor;
|
|
26
|
-
if (a.patch !== b.patch)
|
|
27
|
-
return a.patch > b.patch;
|
|
28
|
-
return false;
|
|
29
|
-
}
|
|
30
13
|
async function promptYesNo(message) {
|
|
31
14
|
if (!process.stdin.isTTY || !process.stdout.isTTY)
|
|
32
15
|
return false;
|
|
@@ -36,16 +19,14 @@ async function promptYesNo(message) {
|
|
|
36
19
|
return answer.trim().toLowerCase().startsWith("y");
|
|
37
20
|
}
|
|
38
21
|
function cachePath() {
|
|
39
|
-
|
|
40
|
-
return path.join(home, CACHE_FILE);
|
|
22
|
+
return path.join(prodHomeDir(), CACHE_FILE);
|
|
41
23
|
}
|
|
42
24
|
function readCache() {
|
|
43
25
|
try {
|
|
44
26
|
const p = cachePath();
|
|
45
27
|
if (!fs.existsSync(p))
|
|
46
28
|
return undefined;
|
|
47
|
-
|
|
48
|
-
return JSON.parse(raw);
|
|
29
|
+
return JSON.parse(fs.readFileSync(p, "utf8"));
|
|
49
30
|
}
|
|
50
31
|
catch {
|
|
51
32
|
return undefined;
|
|
@@ -53,23 +34,34 @@ function readCache() {
|
|
|
53
34
|
}
|
|
54
35
|
function writeCache(next) {
|
|
55
36
|
try {
|
|
56
|
-
|
|
57
|
-
fs.mkdirSync(home, { recursive: true });
|
|
37
|
+
fs.mkdirSync(prodHomeDir(), { recursive: true });
|
|
58
38
|
fs.writeFileSync(cachePath(), JSON.stringify(next), "utf8");
|
|
59
39
|
}
|
|
60
40
|
catch {
|
|
61
|
-
//
|
|
41
|
+
// ignore
|
|
62
42
|
}
|
|
63
43
|
}
|
|
44
|
+
/** npm-global / unknown path — keep TTY prompt behavior. Managed uses hooks instead. */
|
|
64
45
|
export async function maybeCheckForUpdateOnRun() {
|
|
46
|
+
if (detectInstallKind() === "managed") {
|
|
47
|
+
const { activated } = runManagedCliEntryHooks({ allowActivate: true });
|
|
48
|
+
if (activated) {
|
|
49
|
+
console.log(`[agent-dealer] Activated managed version ${activated}`);
|
|
50
|
+
console.log("Re-run your command to use the new version.");
|
|
51
|
+
return { upgraded: true };
|
|
52
|
+
}
|
|
53
|
+
return { upgraded: false };
|
|
54
|
+
}
|
|
65
55
|
if (process.env.AGENT_DEALER_DISABLE_UPGRADE_CHECK === "1") {
|
|
66
56
|
return { upgraded: false };
|
|
67
57
|
}
|
|
58
|
+
if (process.env.AGENT_DEALER_DISABLE_AUTOUPDATER === "1") {
|
|
59
|
+
return { upgraded: false };
|
|
60
|
+
}
|
|
68
61
|
const current = getVersion();
|
|
69
62
|
const cache = readCache();
|
|
70
63
|
if (cache?.checkedAt && Date.now() - cache.checkedAt < CHECK_THROTTLE_MS) {
|
|
71
|
-
|
|
72
|
-
if (cache.latestVersion && isNewer(cache.latestVersion, current)) {
|
|
64
|
+
if (cache.latestVersion && compareSemver(cache.latestVersion, current) > 0) {
|
|
73
65
|
console.log(`Update available: ${current} -> ${cache.latestVersion}`);
|
|
74
66
|
const autoUpgrade = ["1", "true", "yes"].includes(process.env.AGENT_DEALER_AUTO_UPGRADE?.trim().toLowerCase() ?? "");
|
|
75
67
|
if (autoUpgrade) {
|
|
@@ -79,9 +71,9 @@ export async function maybeCheckForUpdateOnRun() {
|
|
|
79
71
|
const canPrompt = process.stdin.isTTY && process.stdout.isTTY;
|
|
80
72
|
if (!canPrompt) {
|
|
81
73
|
console.log(`Run: agent-dealer upgrade`);
|
|
74
|
+
console.log("Tip: agent-dealer install # managed auto-updates");
|
|
82
75
|
return { upgraded: false };
|
|
83
76
|
}
|
|
84
|
-
// Prompt at most once per throttle window to avoid nagging.
|
|
85
77
|
const promptAgeMs = cache.promptedAt ? Date.now() - cache.promptedAt : Infinity;
|
|
86
78
|
if (promptAgeMs >= CHECK_THROTTLE_MS) {
|
|
87
79
|
writeCache({ ...cache, promptedAt: Date.now() });
|
|
@@ -89,20 +81,16 @@ export async function maybeCheckForUpdateOnRun() {
|
|
|
89
81
|
if (!ok)
|
|
90
82
|
return { upgraded: false };
|
|
91
83
|
}
|
|
92
|
-
else {
|
|
93
|
-
// Recently prompted; do not prompt again.
|
|
94
|
-
}
|
|
95
84
|
const code = await installAgentDealerVersion(cache.latestVersion);
|
|
96
85
|
return { upgraded: code === 0 };
|
|
97
86
|
}
|
|
98
87
|
return { upgraded: false };
|
|
99
88
|
}
|
|
100
89
|
const latest = await fetchLatestPublishedVersion(PKG_NAME);
|
|
101
|
-
// Always write checkedAt so we don't hammer the registry on repeated failures.
|
|
102
90
|
writeCache({ checkedAt: Date.now(), latestVersion: latest, promptedAt: cache?.promptedAt });
|
|
103
91
|
if (!latest || latest === current)
|
|
104
92
|
return { upgraded: false };
|
|
105
|
-
if (
|
|
93
|
+
if (compareSemver(latest, current) <= 0)
|
|
106
94
|
return { upgraded: false };
|
|
107
95
|
const autoUpgrade = ["1", "true", "yes"].includes(process.env.AGENT_DEALER_AUTO_UPGRADE?.trim().toLowerCase() ?? "");
|
|
108
96
|
console.log(`Update available: ${current} -> ${latest}`);
|
package/dist/upgrade.d.ts
CHANGED
package/dist/upgrade.js
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import readline from "node:readline";
|
|
3
|
+
import { activateVersion, compareSemver, detectInstallKind, fetchLatestVersion, installCliVersionToPrefix, readCurrentManagedVersion, } from "./managed/index.js";
|
|
3
4
|
import { fetchLatestPublishedVersion } from "./npm-registry.js";
|
|
5
|
+
import { getVersion } from "./version.js";
|
|
4
6
|
const PKG_NAME = "agent-dealer";
|
|
5
7
|
export function printUpgradeHelp() {
|
|
6
8
|
console.log(`Usage:
|
|
7
|
-
agent-dealer upgrade [--to VERSION] [--yes]
|
|
9
|
+
agent-dealer upgrade [--to VERSION] [--yes] [--check]
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
Managed install: download into ~/.agent-dealer/versions and activate.
|
|
12
|
+
npm-global: npm install -g agent-dealer@VERSION.`);
|
|
10
13
|
}
|
|
11
14
|
function parseBooleanLike(s) {
|
|
12
15
|
const v = s?.trim().toLowerCase();
|
|
@@ -32,14 +35,53 @@ async function runNpmGlobalInstall(version) {
|
|
|
32
35
|
}
|
|
33
36
|
export async function runUpgrade(options = {}) {
|
|
34
37
|
const toVersion = options.toVersion?.trim();
|
|
38
|
+
if (detectInstallKind() === "managed") {
|
|
39
|
+
const current = readCurrentManagedVersion() ?? getVersion();
|
|
40
|
+
const latest = toVersion ?? (await fetchLatestVersion());
|
|
41
|
+
if (!latest) {
|
|
42
|
+
console.error(`Could not resolve latest version for ${PKG_NAME}.`);
|
|
43
|
+
return 1;
|
|
44
|
+
}
|
|
45
|
+
console.log(`Current: ${current}`);
|
|
46
|
+
console.log(`Latest: ${latest}`);
|
|
47
|
+
console.log("Install: managed");
|
|
48
|
+
if (!toVersion && compareSemver(latest, current) <= 0) {
|
|
49
|
+
console.log("Already on the latest version.");
|
|
50
|
+
return 0;
|
|
51
|
+
}
|
|
52
|
+
if (options.check) {
|
|
53
|
+
console.log("Update available. Run: agent-dealer upgrade");
|
|
54
|
+
return 0;
|
|
55
|
+
}
|
|
56
|
+
console.log(`Upgrading managed install ${PKG_NAME} → ${latest} ...`);
|
|
57
|
+
const result = await installCliVersionToPrefix(latest);
|
|
58
|
+
if (!result.ok) {
|
|
59
|
+
console.error(`Upgrade failed: ${result.error}`);
|
|
60
|
+
return 1;
|
|
61
|
+
}
|
|
62
|
+
activateVersion(latest);
|
|
63
|
+
console.log("Upgrade complete. Restart any running agent-dealer process.");
|
|
64
|
+
return 0;
|
|
65
|
+
}
|
|
35
66
|
let version = toVersion ?? (await fetchLatestPublishedVersion(PKG_NAME));
|
|
36
67
|
if (!version) {
|
|
37
68
|
console.error(`Could not resolve latest version for ${PKG_NAME}.`);
|
|
38
69
|
return 1;
|
|
39
70
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
71
|
+
console.log(`Current: ${getVersion()}`);
|
|
72
|
+
console.log(`Latest: ${version}`);
|
|
73
|
+
console.log("Install: npm-global (or unknown)");
|
|
74
|
+
if (options.check) {
|
|
75
|
+
if (compareSemver(version, getVersion()) > 0) {
|
|
76
|
+
console.log("Update available. Run: agent-dealer upgrade");
|
|
77
|
+
console.log("Tip: agent-dealer install # managed auto-updates (data unchanged)");
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
console.log("Already on the latest version.");
|
|
81
|
+
}
|
|
82
|
+
return 0;
|
|
83
|
+
}
|
|
84
|
+
const autoYes = options.yes ?? parseBooleanLike(process.env.AGENT_DEALER_UPGRADE_YES) ?? false;
|
|
43
85
|
if (!autoYes) {
|
|
44
86
|
const ok = await confirmPrompt(`Upgrade ${PKG_NAME} to ${version} via "npm install -g ${PKG_NAME}@${version}"? [y/N] `);
|
|
45
87
|
if (!ok) {
|
|
@@ -50,10 +92,17 @@ export async function runUpgrade(options = {}) {
|
|
|
50
92
|
const code = await runNpmGlobalInstall(version);
|
|
51
93
|
if (code === 0) {
|
|
52
94
|
console.log(`Upgraded to ${PKG_NAME}@${version}. Re-run your command to use the new version.`);
|
|
95
|
+
console.log("Tip: agent-dealer install # switch CLI to managed auto-updates (data unchanged)");
|
|
53
96
|
}
|
|
54
97
|
return code;
|
|
55
98
|
}
|
|
56
|
-
// Used by update-check: prompts happen elsewhere, so we only do the install.
|
|
57
99
|
export async function installAgentDealerVersion(version) {
|
|
100
|
+
if (detectInstallKind() === "managed") {
|
|
101
|
+
const result = await installCliVersionToPrefix(version);
|
|
102
|
+
if (!result.ok)
|
|
103
|
+
return 1;
|
|
104
|
+
activateVersion(version);
|
|
105
|
+
return 0;
|
|
106
|
+
}
|
|
58
107
|
return runNpmGlobalInstall(version);
|
|
59
108
|
}
|