Merge pull request #1111 from artuskg/fix/cli-install-version-suffix
macos: keep CLI install build suffix
This commit is contained in:
@@ -6,12 +6,8 @@ Docs: https://docs.clawd.bot
|
|||||||
|
|
||||||
### Changes
|
### Changes
|
||||||
- macOS: strip prerelease/build suffixes when parsing gateway semver patches. (#1110) — thanks @zerone0x.
|
- macOS: strip prerelease/build suffixes when parsing gateway semver patches. (#1110) — thanks @zerone0x.
|
||||||
- Models: add Kimi Code provider onboarding and docs. (#1085) — thanks @dan-dr.
|
- macOS: keep CLI install pinned to the full build suffix. (#1111) — thanks @artuskg.
|
||||||
|
|
||||||
### Fixes
|
|
||||||
- Matrix: send voice/image-specific media payloads and keep legacy poll parsing. (#1088) — thanks @sibbl.
|
|
||||||
- Telegram: allow media-only message tool sends to request voice notes via `asVoice`. (#1099) — thanks @mukhtharcm.
|
|
||||||
- Discord: soften logs for expired interactions and stale component clicks.
|
|
||||||
## 2026.1.16-2
|
## 2026.1.16-2
|
||||||
|
|
||||||
### Changes
|
### Changes
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ enum CLIInstaller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static func install(statusHandler: @escaping @MainActor @Sendable (String) async -> Void) async {
|
static func install(statusHandler: @escaping @MainActor @Sendable (String) async -> Void) async {
|
||||||
let expected = GatewayEnvironment.expectedGatewayVersion()?.description ?? "latest"
|
let expected = GatewayEnvironment.expectedGatewayVersionString() ?? "latest"
|
||||||
let prefix = Self.installPrefix()
|
let prefix = Self.installPrefix()
|
||||||
await statusHandler("Installing clawdbot CLI…")
|
await statusHandler("Installing clawdbot CLI…")
|
||||||
let cmd = self.installScriptCommand(version: expected, prefix: prefix)
|
let cmd = self.installScriptCommand(version: expected, prefix: prefix)
|
||||||
|
|||||||
@@ -80,8 +80,13 @@ enum GatewayEnvironment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static func expectedGatewayVersion() -> Semver? {
|
static func expectedGatewayVersion() -> Semver? {
|
||||||
|
Semver.parse(self.expectedGatewayVersionString())
|
||||||
|
}
|
||||||
|
|
||||||
|
static func expectedGatewayVersionString() -> String? {
|
||||||
let bundleVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
|
let bundleVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
|
||||||
return Semver.parse(bundleVersion)
|
let trimmed = bundleVersion?.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
|
return (trimmed?.isEmpty == false) ? trimmed : nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exposed for tests so we can inject fake version checks without rewriting bundle metadata.
|
// Exposed for tests so we can inject fake version checks without rewriting bundle metadata.
|
||||||
@@ -100,6 +105,7 @@ enum GatewayEnvironment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let expected = self.expectedGatewayVersion()
|
let expected = self.expectedGatewayVersion()
|
||||||
|
let expectedString = self.expectedGatewayVersionString()
|
||||||
|
|
||||||
let projectRoot = CommandResolver.projectRoot()
|
let projectRoot = CommandResolver.projectRoot()
|
||||||
let projectEntrypoint = CommandResolver.gatewayEntrypoint(in: projectRoot)
|
let projectEntrypoint = CommandResolver.gatewayEntrypoint(in: projectRoot)
|
||||||
@@ -110,8 +116,8 @@ enum GatewayEnvironment {
|
|||||||
kind: .missingNode,
|
kind: .missingNode,
|
||||||
nodeVersion: nil,
|
nodeVersion: nil,
|
||||||
gatewayVersion: nil,
|
gatewayVersion: nil,
|
||||||
requiredGateway: expected?.description,
|
requiredGateway: expectedString,
|
||||||
message: RuntimeLocator.describeFailure(err))
|
message: RuntimeLocator.describeFailure(err))
|
||||||
case let .success(runtime):
|
case let .success(runtime):
|
||||||
let gatewayBin = CommandResolver.clawdbotExecutable()
|
let gatewayBin = CommandResolver.clawdbotExecutable()
|
||||||
|
|
||||||
@@ -120,7 +126,7 @@ enum GatewayEnvironment {
|
|||||||
kind: .missingGateway,
|
kind: .missingGateway,
|
||||||
nodeVersion: runtime.version.description,
|
nodeVersion: runtime.version.description,
|
||||||
gatewayVersion: nil,
|
gatewayVersion: nil,
|
||||||
requiredGateway: expected?.description,
|
requiredGateway: expectedString,
|
||||||
message: "clawdbot CLI not found in PATH; install the CLI.")
|
message: "clawdbot CLI not found in PATH; install the CLI.")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,13 +134,14 @@ enum GatewayEnvironment {
|
|||||||
?? self.readLocalGatewayVersion(projectRoot: projectRoot)
|
?? self.readLocalGatewayVersion(projectRoot: projectRoot)
|
||||||
|
|
||||||
if let expected, let installed, !installed.compatible(with: expected) {
|
if let expected, let installed, !installed.compatible(with: expected) {
|
||||||
|
let expectedText = expectedString ?? expected.description
|
||||||
return GatewayEnvironmentStatus(
|
return GatewayEnvironmentStatus(
|
||||||
kind: .incompatible(found: installed.description, required: expected.description),
|
kind: .incompatible(found: installed.description, required: expectedText),
|
||||||
nodeVersion: runtime.version.description,
|
nodeVersion: runtime.version.description,
|
||||||
gatewayVersion: installed.description,
|
gatewayVersion: installed.description,
|
||||||
requiredGateway: expected.description,
|
requiredGateway: expectedText,
|
||||||
message: """
|
message: """
|
||||||
Gateway version \(installed.description) is incompatible with app \(expected.description);
|
Gateway version \(installed.description) is incompatible with app \(expectedText);
|
||||||
install or update the global package.
|
install or update the global package.
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
@@ -152,7 +159,7 @@ enum GatewayEnvironment {
|
|||||||
kind: .ok,
|
kind: .ok,
|
||||||
nodeVersion: runtime.version.description,
|
nodeVersion: runtime.version.description,
|
||||||
gatewayVersion: gatewayVersionText,
|
gatewayVersion: gatewayVersionText,
|
||||||
requiredGateway: expected?.description,
|
requiredGateway: expectedString,
|
||||||
message: "Node \(runtime.version.description); gateway \(gatewayVersionText) \(gatewayLabelText)")
|
message: "Node \(runtime.version.description); gateway \(gatewayVersionText) \(gatewayLabelText)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -220,8 +227,18 @@ enum GatewayEnvironment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static func installGlobal(version: Semver?, statusHandler: @escaping @Sendable (String) -> Void) async {
|
static func installGlobal(version: Semver?, statusHandler: @escaping @Sendable (String) -> Void) async {
|
||||||
|
await self.installGlobal(versionString: version?.description, statusHandler: statusHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func installGlobal(versionString: String?, statusHandler: @escaping @Sendable (String) -> Void) async {
|
||||||
let preferred = CommandResolver.preferredPaths().joined(separator: ":")
|
let preferred = CommandResolver.preferredPaths().joined(separator: ":")
|
||||||
let target = version?.description ?? "latest"
|
let trimmed = versionString?.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
|
let target: String
|
||||||
|
if let trimmed, !trimmed.isEmpty {
|
||||||
|
target = trimmed
|
||||||
|
} else {
|
||||||
|
target = "latest"
|
||||||
|
}
|
||||||
let npm = CommandResolver.findExecutable(named: "npm")
|
let npm = CommandResolver.findExecutable(named: "npm")
|
||||||
let pnpm = CommandResolver.findExecutable(named: "pnpm")
|
let pnpm = CommandResolver.findExecutable(named: "pnpm")
|
||||||
let bun = CommandResolver.findExecutable(named: "bun")
|
let bun = CommandResolver.findExecutable(named: "bun")
|
||||||
|
|||||||
@@ -155,7 +155,7 @@ struct OnboardingView: View {
|
|||||||
|
|
||||||
var canAdvance: Bool { !self.isWizardBlocking }
|
var canAdvance: Bool { !self.isWizardBlocking }
|
||||||
var devLinkCommand: String {
|
var devLinkCommand: String {
|
||||||
let version = GatewayEnvironment.expectedGatewayVersion()?.description ?? "latest"
|
let version = GatewayEnvironment.expectedGatewayVersionString() ?? "latest"
|
||||||
return "npm install -g clawdbot@\(version)"
|
return "npm install -g clawdbot@\(version)"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ import Testing
|
|||||||
|
|
||||||
@Test func expectedGatewayVersionFromStringUsesParser() {
|
@Test func expectedGatewayVersionFromStringUsesParser() {
|
||||||
#expect(GatewayEnvironment.expectedGatewayVersion(from: "v9.1.2") == Semver(major: 9, minor: 1, patch: 2))
|
#expect(GatewayEnvironment.expectedGatewayVersion(from: "v9.1.2") == Semver(major: 9, minor: 1, patch: 2))
|
||||||
|
#expect(GatewayEnvironment.expectedGatewayVersion(from: "2026.1.11-4") == Semver(major: 2026, minor: 1, patch: 11))
|
||||||
#expect(GatewayEnvironment.expectedGatewayVersion(from: nil) == nil)
|
#expect(GatewayEnvironment.expectedGatewayVersion(from: nil) == nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { prependSystemEvents } from "./session-updates.js";
|
|||||||
describe("prependSystemEvents", () => {
|
describe("prependSystemEvents", () => {
|
||||||
it("adds a UTC timestamp to queued system events", async () => {
|
it("adds a UTC timestamp to queued system events", async () => {
|
||||||
vi.useFakeTimers();
|
vi.useFakeTimers();
|
||||||
const timestamp = new Date("2026-01-12T20:19:17");
|
const timestamp = new Date("2026-01-12T20:19:17Z");
|
||||||
vi.setSystemTime(timestamp);
|
vi.setSystemTime(timestamp);
|
||||||
|
|
||||||
enqueueSystemEvent("Model switched.", { sessionKey: "agent:main:main" });
|
enqueueSystemEvent("Model switched.", { sessionKey: "agent:main:main" });
|
||||||
|
|||||||
Reference in New Issue
Block a user