mirror of
https://github.com/MoonshotAI/kimi-code.git
synced 2026-07-10 01:39:25 +00:00
* feat(kimi-desktop): add Electron desktop client wrapping kimi-web
New apps/kimi-desktop — a thin Electron shell + process manager around
the existing web UI. It reuses kimi-code's shared daemon: it runs the
bundled SEA's `server run` (the same ensureDaemon reuse-or-spawn flow as
`kimi web`), reads ~/.kimi-code/server/lock for the real origin, and
loads the SEA-served kimi-web same-origin. The daemon is left running on
quit so the CLI / browser / TUI keep sharing it.
- main process: ensure-server (run SEA, read lock, confirm healthz),
sea-path (dev vs packaged), window + native menu + window-state +
loading/error screens
- packaging: electron-builder config; before-pack stages the
matching-platform SEA into <resources>/bin/<target>
- CI: desktop-build workflow builds unsigned mac/win/linux installers,
each runner building its own SEA
- workspace wiring: register in flake.nix, allow electron postinstall
(onlyBuiltDependencies), root dev:desktop + typecheck entries
v1 is unsigned, default icon, no auto-update.
* feat(kimi-desktop): sign + notarize macOS builds
Unsigned macOS builds are blocked by Gatekeeper ("app is damaged") once
transferred to another Mac. Add Developer ID signing + Apple notarization,
mirroring the TUI native build:
- build/entitlements.mac.plist: hardened-runtime entitlements (allow-jit,
disable-library-validation for koffi/clipboard, etc.) applied to the app
and — via entitlementsInherit — the nested SEA backend
- electron-builder.config.cjs (replaces .yml): hardenedRuntime + entitlements;
signing and notarization are env-driven (CSC_* + KIMI_DESKTOP_NOTARIZE +
APPLE_API_* ), so the same config builds unsigned locally or signed+notarized
- desktop-build CI: sign-macos input reuses the existing macos-keychain-setup
action + APPLE_* secrets, notarizes via the notary API key
- README: document signing, the Developer-ID requirement, and the
"don't rename the .app" gotcha
Verified locally that electron-builder signs both the app and the nested SEA
with hardened runtime + the entitlements, and the signed app still launches and
serves the web UI. Notarization itself needs a Developer ID cert (CI / a machine
that has one).
* feat(kimi-desktop): rename product to Kimi Code Desktop
productName / window title / menu label / error-screen text all use
"Kimi Code Desktop" so the bundle name matches its executable (a
mismatch from manual renaming is itself reported as "damaged").
* ci(kimi-desktop): build and attach desktop installers in the release pipeline
Make desktop-build.yml reusable (workflow_call) and invoke it from the
release workflow, mirroring the native-build pipeline, so each release
also attaches signed+notarized macOS, Windows and Linux desktop
installers to the GitHub Release.
* feat(kimi-desktop): brand the desktop as an internal testing build
- Add an inline 'internal testing build' tag next to the Kimi Code brand
in the sidebar header, shown only inside the desktop app.
- Use a hidden native title bar on macOS with the traffic lights folded
into the sidebar header, and pin the window title to the product name.
- Ship the Kimi app icon for macOS and Linux builds.
Desktop detection is runtime (a query hint from the Electron shell,
persisted in sessionStorage) so the branding appears even when the
window is served by an already-running shared daemon.
* docs(kimi-desktop): update v1 scope now that the app icon ships
* feat(kimi-desktop): add the Kimi app icon for Windows builds
* chore(nix): update pnpmDeps hash after lockfile refresh
* ci(kimi-desktop): build desktop on release but do not attach to GitHub Release
The desktop build is an internal-testing artifact (branded as such), so
keep it as a CI artifact for internal download instead of publishing it
to the public GitHub Release.
* chore(kimi-desktop): mark installers as internal pre-release builds
Rename the packaged artifacts to KCD-Internal-<version>-<arch>.<ext> and
bump the version to the 0.1.1-internal.0 pre-release, so a leaked or
forwarded installer file is not mistaken for an official public release.
* feat(kimi-desktop): strengthen the internal-build tag wording
Change the sidebar tag to 'Internal testing · do not distribute' /
'内部测试 · 禁止外传' so the no-distribution intent is explicit.
* feat(kimi-desktop): tweak internal-build tag to '仅供内部测试'
* fix(kimi-desktop): pass the server token to the web UI on launch
Read the daemon's persistent bearer token from <KIMI_CODE_HOME>/server.token
and carry it in the URL fragment (#token=), matching how 'kimi web' opens
the Web UI. Without this, a fresh launch (no saved credential) boots the
web UI without a token, hits 401, and falls into the manual token dialog
even though the desktop started the daemon itself.
Addresses review feedback on the desktop URL.
255 lines
8.3 KiB
Nix
255 lines
8.3 KiB
Nix
{
|
|
description = "Kimi Code CLI";
|
|
|
|
inputs = {
|
|
# Pinned to the 25.11 release channel because nixpkgs-unstable currently
|
|
# ships nodejs_24 = 24.14.1, which trips the >= 24.15.0 floor that the
|
|
# native SEA build enforces (see apps/kimi-code/scripts/native/build.mjs).
|
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11";
|
|
};
|
|
|
|
outputs =
|
|
{ self, nixpkgs }:
|
|
let
|
|
lib = nixpkgs.lib;
|
|
|
|
systems = [
|
|
"x86_64-linux"
|
|
"aarch64-linux"
|
|
"x86_64-darwin"
|
|
"aarch64-darwin"
|
|
];
|
|
|
|
forAllSystems =
|
|
f:
|
|
lib.genAttrs systems (
|
|
system:
|
|
f (import nixpkgs {
|
|
inherit system;
|
|
})
|
|
);
|
|
|
|
minNodeVersion = "24.15.0";
|
|
|
|
# Hardcode to Node.js 24.x; fail the evaluation if the pinned nixpkgs
|
|
# does not offer a new enough 24.x.
|
|
nodejsFor =
|
|
pkgs:
|
|
let
|
|
node = pkgs.nodejs_24;
|
|
in
|
|
if lib.versionAtLeast node.version minNodeVersion then
|
|
node
|
|
else
|
|
throw ''
|
|
Kimi Code requires Node.js >= ${minNodeVersion},
|
|
but nixpkgs only offers ${node.version}.
|
|
Pin a newer nixpkgs revision or update minNodeVersion in flake.nix.
|
|
'';
|
|
|
|
pnpmFor =
|
|
pkgs:
|
|
pkgs.pnpm_10.override {
|
|
nodejs = nodejsFor pkgs;
|
|
};
|
|
|
|
# -------------------------------------------------------------------
|
|
# Workspace members (kept in sync with pnpm-workspace.yaml).
|
|
#
|
|
# HARD REQUIREMENT: whenever you add or remove a workspace package,
|
|
# you MUST update both lists below. Missing a path will break the Nix
|
|
# build (src fileset silently drops files); missing a name will break
|
|
# pnpmConfigHook (dependencies for that workspace won't be fetched).
|
|
# -------------------------------------------------------------------
|
|
workspacePaths = [
|
|
./packages/acp-adapter
|
|
./packages/agent-core
|
|
./packages/server
|
|
./packages/server-e2e
|
|
./packages/kaos
|
|
./packages/kimi-migration-legacy
|
|
./packages/kosong
|
|
./packages/migration-legacy
|
|
./packages/node-sdk
|
|
./packages/oauth
|
|
./packages/protocol
|
|
./packages/telemetry
|
|
./apps/kimi-code
|
|
./apps/kimi-desktop
|
|
./apps/kimi-web
|
|
./apps/vis
|
|
./apps/vis/server
|
|
./apps/vis/web
|
|
./docs
|
|
];
|
|
|
|
workspaceNames = [
|
|
"@moonshot-ai/acp-adapter"
|
|
"@moonshot-ai/agent-core"
|
|
"@moonshot-ai/server"
|
|
"@moonshot-ai/server-e2e"
|
|
"@moonshot-ai/kaos"
|
|
"@moonshot-ai/kosong"
|
|
"@moonshot-ai/migration-legacy"
|
|
"@moonshot-ai/kimi-code-sdk"
|
|
"@moonshot-ai/kimi-code-oauth"
|
|
"@moonshot-ai/protocol"
|
|
"@moonshot-ai/kimi-telemetry"
|
|
"@moonshot-ai/kimi-code"
|
|
"@moonshot-ai/kimi-desktop"
|
|
"@moonshot-ai/kimi-web"
|
|
"@moonshot-ai/vis"
|
|
"@moonshot-ai/vis-server"
|
|
"@moonshot-ai/vis-web"
|
|
"kimi-code-docs"
|
|
"kimi-migration-legacy"
|
|
];
|
|
in
|
|
{
|
|
packages = forAllSystems (
|
|
pkgs:
|
|
let
|
|
nodejs = nodejsFor pkgs;
|
|
pnpm = pnpmFor pkgs;
|
|
appPackageJson = builtins.fromJSON (builtins.readFile ./apps/kimi-code/package.json);
|
|
nativeTarget =
|
|
if pkgs.stdenv.hostPlatform.isLinux && pkgs.stdenv.hostPlatform.isAarch64 then
|
|
"linux-arm64"
|
|
else if pkgs.stdenv.hostPlatform.isLinux then
|
|
"linux-x64"
|
|
else if pkgs.stdenv.hostPlatform.isDarwin && pkgs.stdenv.hostPlatform.isAarch64 then
|
|
"darwin-arm64"
|
|
else if pkgs.stdenv.hostPlatform.isDarwin then
|
|
"darwin-x64"
|
|
else
|
|
throw "Unsupported Kimi Code native target for ${pkgs.stdenv.hostPlatform.system}";
|
|
|
|
kimi-code = pkgs.stdenv.mkDerivation (finalAttrs: {
|
|
pname = "kimi-code";
|
|
version = appPackageJson.version;
|
|
|
|
src = lib.fileset.toSource {
|
|
root = ./.;
|
|
fileset = lib.fileset.unions (
|
|
[
|
|
./build
|
|
./.npmrc
|
|
./.nvmrc
|
|
./package.json
|
|
./pnpm-lock.yaml
|
|
./pnpm-workspace.yaml
|
|
./tsconfig.json
|
|
./vitest.config.ts
|
|
./LICENSE
|
|
]
|
|
++ workspacePaths
|
|
);
|
|
};
|
|
|
|
pnpmWorkspaces = [ "." ] ++ workspaceNames;
|
|
|
|
pnpmDeps = pkgs.fetchPnpmDeps {
|
|
inherit (finalAttrs) pname version src pnpmWorkspaces;
|
|
inherit pnpm;
|
|
fetcherVersion = 3;
|
|
hash = "sha256-oratz8x67ZEJGTiNy+s4XaKe0TtpRKh63aIqkV79vvM=";
|
|
};
|
|
|
|
nativeBuildInputs = [
|
|
nodejs
|
|
pnpm
|
|
(pkgs.pnpmConfigHook.override { inherit pnpm; })
|
|
pkgs.makeWrapper
|
|
]
|
|
# The SEA inject step (postject) invalidates the macOS code
|
|
# signature on the copied Node executable; build.mjs then re-applies
|
|
# an ad-hoc signature via `codesign`. The Nix darwin sandbox does
|
|
# not expose /usr/bin/codesign, so we supply nixpkgs' ad-hoc-only
|
|
# replacement instead.
|
|
++ lib.optionals pkgs.stdenv.hostPlatform.isDarwin [
|
|
pkgs.darwin.sigtool
|
|
];
|
|
|
|
# The SEA binary is produced by `postject`-injecting a blob into a
|
|
# plain Node executable. Stripping rewrites section tables and can
|
|
# invalidate the injected blob's offsets, so leave the binary
|
|
# untouched after the build.
|
|
dontStrip = true;
|
|
|
|
buildPhase = ''
|
|
runHook preBuild
|
|
export KIMI_CODE_BUILD_TARGET=${nativeTarget}
|
|
${lib.optionalString pkgs.stdenv.hostPlatform.isDarwin ''
|
|
# pkgs.darwin.sigtool's codesign supports `--sign -` (ad-hoc)
|
|
# but not the inspection mode (`-dv`) that 05-verify.mjs runs
|
|
# afterwards. Disable the verify step for the Nix build; the
|
|
# release CI keeps it via the unmodified script.
|
|
substituteInPlace apps/kimi-code/scripts/native/build.mjs \
|
|
--replace-fail \
|
|
"await runVerifyStep({ requireGatekeeper: false });" \
|
|
"// runVerifyStep skipped in nix sandbox (sigtool lacks -dv)"
|
|
''}
|
|
# The SEA blob step (scripts/native/02-sea-blob.mjs) embeds the
|
|
# Kimi web assets from apps/kimi-code/dist-web and fails if that
|
|
# directory is missing. Build the web app and stage its assets
|
|
# before producing the native executable.
|
|
pnpm --filter=@moonshot-ai/kimi-web run build
|
|
node apps/kimi-code/scripts/copy-web-assets.mjs
|
|
pnpm --filter=@moonshot-ai/kimi-code run build:native:sea
|
|
runHook postBuild
|
|
'';
|
|
|
|
installPhase = ''
|
|
runHook preInstall
|
|
|
|
install -Dm755 \
|
|
"apps/kimi-code/dist-native/bin/${nativeTarget}/kimi" \
|
|
"$out/bin/kimi"
|
|
|
|
runHook postInstall
|
|
'';
|
|
|
|
postInstall = ''
|
|
wrapProgram $out/bin/kimi --prefix PATH : ${lib.makeBinPath [ pkgs.ripgrep pkgs.fd ]}
|
|
'';
|
|
|
|
meta = {
|
|
description = "Kimi Code CLI";
|
|
homepage = "https://github.com/MoonshotAI/kimi-code";
|
|
license = lib.licenses.mit;
|
|
mainProgram = "kimi";
|
|
platforms = systems;
|
|
};
|
|
});
|
|
in
|
|
{
|
|
inherit kimi-code;
|
|
default = kimi-code;
|
|
}
|
|
);
|
|
|
|
apps = forAllSystems (pkgs: {
|
|
kimi-code = {
|
|
type = "app";
|
|
program = "${self.packages.${pkgs.system}.kimi-code}/bin/kimi";
|
|
};
|
|
default = self.apps.${pkgs.system}.kimi-code;
|
|
});
|
|
|
|
devShells = forAllSystems (pkgs: {
|
|
default =
|
|
let
|
|
nodejs = nodejsFor pkgs;
|
|
pnpm = pnpmFor pkgs;
|
|
in
|
|
pkgs.mkShell {
|
|
packages = [
|
|
nodejs
|
|
pnpm
|
|
pkgs.ripgrep
|
|
pkgs.fd
|
|
];
|
|
};
|
|
});
|
|
};
|
|
}
|