feat(integration): expose declarative harness surface (#185)
* chore(nix): add reproducible OpenPencil flake * fix(nix): patch and smoke-test prebuilt binaries * chore(ci): keep workflow validation in integration follow-up * feat(integration): expose declarative harness surface * ci: run Nix checks for integration surface * test: verify all OpenPencil runtime tools * feat(integration): expose web server package * chore(nix): remove rebase conflict marker * fix(nix): update OpenPencil CLI prebuilt hash * fix(nix): validate source outputs and pin release assets * fix(nix): refresh desktop release hash * fix(nix): refresh Bun dependency hash * fix(nix): normalize Bun hoisted aliases * ci: make headless desktop smoke test deterministic
This commit is contained in:
parent
e77355e193
commit
bb27a7458f
103
.github/workflows/nix-check.yml
vendored
Normal file
103
.github/workflows/nix-check.yml
vendored
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
name: Nix flake checks
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "Cargo.toml"
|
||||
- "Cargo.lock"
|
||||
- "flake.nix"
|
||||
- "flake.lock"
|
||||
- "nix/release-manifest.json"
|
||||
- "nix/integration/**"
|
||||
- "crates/op-host-desktop/assets/icon.png"
|
||||
- ".github/workflows/nix-check.yml"
|
||||
push:
|
||||
branches: ["**"]
|
||||
paths:
|
||||
- "Cargo.toml"
|
||||
- "Cargo.lock"
|
||||
- "flake.nix"
|
||||
- "flake.lock"
|
||||
- "nix/release-manifest.json"
|
||||
- "nix/integration/**"
|
||||
- "crates/op-host-desktop/assets/icon.png"
|
||||
- ".github/workflows/nix-check.yml"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
flake:
|
||||
name: Build and smoke-test prebuilt outputs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Install Nix
|
||||
uses: DeterminateSystems/nix-installer-action@main
|
||||
|
||||
- name: Enable Nix cache
|
||||
uses: DeterminateSystems/magic-nix-cache-action@main
|
||||
|
||||
- name: Evaluate flake checks
|
||||
run: nix flake check --no-build --show-trace
|
||||
|
||||
- name: Build prebuilt packages
|
||||
run: nix build .#prebuilt .#prebuilt-cli --no-link
|
||||
|
||||
- name: Build representative source outputs
|
||||
run: |
|
||||
nix build \
|
||||
.#op-cli \
|
||||
.#web-server \
|
||||
.#web-bundle \
|
||||
.#web-sdk-wasm \
|
||||
.#web-sdk-packages \
|
||||
.#appimage \
|
||||
--no-link
|
||||
|
||||
- name: Verify generated integration sidecar
|
||||
run: nix build .#checks.x86_64-linux.integration-sidecar --no-link
|
||||
|
||||
- name: Install headless display server
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install --yes \
|
||||
libegl1 libgles2 libgl1-mesa-dri libgbm1 mesa-utils xvfb
|
||||
|
||||
- name: Smoke-test prebuilt CLI
|
||||
run: |
|
||||
output="$(nix run .#prebuilt-cli -- --version)"
|
||||
printf '%s\n' "$output"
|
||||
printf '%s\n' "$output" | grep -Eq '^\{"version":"[0-9]+\.[0-9]+\.[0-9]+"\}$'
|
||||
|
||||
- name: Smoke-test prebuilt desktop
|
||||
shell: bash
|
||||
run: |
|
||||
set +e
|
||||
LIBGL_ALWAYS_SOFTWARE=1 \
|
||||
MESA_LOADER_DRIVER_OVERRIDE=llvmpipe \
|
||||
GALLIUM_DRIVER=llvmpipe \
|
||||
timeout 20s xvfb-run -a -s "-screen 0 1280x720x24" nix run .#prebuilt \
|
||||
>/tmp/openpencil-desktop.log 2>&1
|
||||
status=$?
|
||||
set -e
|
||||
|
||||
# A GUI process is expected to remain alive until timeout. Any other
|
||||
# non-zero status means startup failed before the process was usable.
|
||||
if [[ "$status" -ne 0 && "$status" -ne 124 ]]; then
|
||||
cat /tmp/openpencil-desktop.log
|
||||
exit "$status"
|
||||
fi
|
||||
if grep -Eq 'error while loading|cannot open shared object' \
|
||||
/tmp/openpencil-desktop.log; then
|
||||
cat /tmp/openpencil-desktop.log
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Run the clean NixOS runtime test
|
||||
run: nix build .#checks.x86_64-linux.prebuilt-runtime --no-link
|
||||
36
.github/workflows/rust-release.yml
vendored
36
.github/workflows/rust-release.yml
vendored
|
|
@ -870,6 +870,14 @@ jobs:
|
|||
echo "::error::missing op-cli artifacts"
|
||||
exit 1
|
||||
fi
|
||||
for file in \
|
||||
release-files/openpencil-desktop-linux-x86_64.tar.gz \
|
||||
release-files/op-cli-linux-x86_64.tar.gz; do
|
||||
if [ ! -f "$file" ]; then
|
||||
echo "::error::missing Linux x86_64 Nix manifest asset: $file"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
if [ "${#sdk[@]}" -ne 3 ]; then
|
||||
echo "::error::expected 3 SDK tarballs, found ${#sdk[@]}"
|
||||
exit 1
|
||||
|
|
@ -959,6 +967,34 @@ jobs:
|
|||
name: ${{ steps.body.outputs.title }}
|
||||
body_path: release-body.md
|
||||
files: release-files/*
|
||||
- name: Update latest published Nix release manifest
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
version="$OP_VERSION"
|
||||
sri_hash() {
|
||||
printf 'sha256-%s' "$(openssl dgst -sha256 -binary "release-files/$1" | base64 -w0)"
|
||||
}
|
||||
manifest="$(jq -n \
|
||||
--arg version "$version" \
|
||||
--arg desktopHash "$(sri_hash openpencil-desktop-linux-x86_64.tar.gz)" \
|
||||
--arg cliHash "$(sri_hash op-cli-linux-x86_64.tar.gz)" \
|
||||
'{version: $version, desktopHash: $desktopHash, cliHash: $cliHash}')"
|
||||
|
||||
git fetch origin main
|
||||
git switch --create update-release-manifest origin/main
|
||||
printf '%s\n' "$manifest" > nix/release-manifest.json
|
||||
git config user.name github-actions[bot]
|
||||
git config user.email 41898282+github-actions[bot]@users.noreply.github.com
|
||||
git add nix/release-manifest.json
|
||||
if git diff --cached --quiet; then
|
||||
echo "release manifest already current"
|
||||
exit 0
|
||||
fi
|
||||
git commit -m "chore(release): update Nix release manifest for v${version}"
|
||||
git push origin HEAD:main
|
||||
|
||||
package-managers:
|
||||
name: Update Homebrew tap and Scoop bucket
|
||||
|
|
|
|||
22
README.md
22
README.md
|
|
@ -147,6 +147,28 @@ scoop install openpencil
|
|||
|
||||
**Linux / Windows direct download:** [GitHub Releases](https://github.com/ZSeven-W/openpencil/releases) — `.exe` (Windows), `.AppImage` / `.deb` (Linux)
|
||||
|
||||
**Nix (Linux x86_64):**
|
||||
|
||||
```bash
|
||||
nix develop
|
||||
nix run . # launch the desktop app
|
||||
nix build .#openpencil # native web host + CanvasKit web bundle
|
||||
nix build .#op-cli # the `op` CLI
|
||||
nix build .#prebuilt # use the matching upstream desktop archive
|
||||
nix build .#prebuilt-cli # use the matching upstream CLI archive
|
||||
nix build .#web-server # native GL-free web server + web bundle
|
||||
nix build .#runtime-prebuilt # prebuilt desktop + `op` CLI runtime
|
||||
nix build .#web-sdk-packages # npm tarballs for the web SDKs
|
||||
nix build .#appimage # portable desktop AppImage
|
||||
```
|
||||
|
||||
The flake uses the pinned Rust toolchain from `rust-toolchain.toml` and is
|
||||
currently published for `x86_64-linux`. A Debian package is not produced by
|
||||
the flake yet; use the upstream release artifacts when a `.deb` is required.
|
||||
The `prebuilt` outputs require the upstream GitHub release for the workspace
|
||||
version to publish matching Linux archives; otherwise use the source-built
|
||||
outputs above.
|
||||
|
||||
**CLI (`op`):**
|
||||
|
||||
```bash
|
||||
|
|
|
|||
1117
flake.lock
Normal file
1117
flake.lock
Normal file
File diff suppressed because it is too large
Load diff
649
flake.nix
Normal file
649
flake.nix
Normal file
|
|
@ -0,0 +1,649 @@
|
|||
{
|
||||
description = "OpenPencil native editor, web host, and web SDK flake";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
flake-parts.url = "github:hercules-ci/flake-parts";
|
||||
crane.url = "github:ipetkov/crane";
|
||||
rust-overlay = {
|
||||
url = "github:oxalica/rust-overlay";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
rs-harbor = {
|
||||
url = "git+https://codeberg.org/caniko/rs-harbor.git?ref=trunk";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
inputs.flake-parts.follows = "flake-parts";
|
||||
inputs.crane.follows = "crane";
|
||||
inputs.rust-overlay.follows = "rust-overlay";
|
||||
};
|
||||
py-harbor = {
|
||||
url = "git+https://codeberg.org/caniko/py-harbor.git?ref=trunk";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
js-harbor = {
|
||||
url = "git+https://codeberg.org/caniko/js-harbor.git?ref=trunk";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
inputs.flake-parts.follows = "flake-parts";
|
||||
};
|
||||
|
||||
nix-appimage.url = "github:ralismark/nix-appimage";
|
||||
|
||||
nix-pklx = {
|
||||
url = "git+https://codeberg.org/caniko/nix-pklx.git";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
agent = {
|
||||
url = "github:ZSeven-W/agent-rs/5c0f9506e27be5b4f29cd2c1093e2858ad0fa20c";
|
||||
flake = false;
|
||||
};
|
||||
casement = {
|
||||
url = "github:ZSeven-W/casement/451173eb3353a4166d2d0f241f2e0606051064bd";
|
||||
flake = false;
|
||||
};
|
||||
jian = {
|
||||
url = "github:ZSeven-W/jian/df2376a018acbd8ec8d9fac58b05dde14a405aca";
|
||||
flake = false;
|
||||
};
|
||||
};
|
||||
|
||||
outputs = inputs @ {
|
||||
self,
|
||||
flake-parts,
|
||||
nixpkgs,
|
||||
crane,
|
||||
rust-overlay,
|
||||
rs-harbor,
|
||||
py-harbor,
|
||||
js-harbor,
|
||||
nix-appimage,
|
||||
nix-pklx,
|
||||
agent,
|
||||
casement,
|
||||
jian,
|
||||
...
|
||||
}:
|
||||
flake-parts.lib.mkFlake {inherit inputs;} {
|
||||
systems = ["x86_64-linux"];
|
||||
|
||||
flake = {
|
||||
lib.integrationManifest = import ./nix/integration/openpencil.nix;
|
||||
};
|
||||
|
||||
perSystem = {system, ...}: let
|
||||
lib = pkgs.lib;
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
overlays = [(import rust-overlay)];
|
||||
};
|
||||
version = (builtins.fromTOML (builtins.readFile ./Cargo.toml)).workspace.package.version;
|
||||
releaseManifest = builtins.fromJSON (builtins.readFile ./nix/release-manifest.json);
|
||||
releaseVersion = releaseManifest.version;
|
||||
|
||||
toolchain = rs-harbor.lib.mkToolchain {
|
||||
inherit pkgs;
|
||||
toolchainFile = ./rust-toolchain.toml;
|
||||
cache.enable = false;
|
||||
};
|
||||
craneLib = toolchain.craneLib;
|
||||
rustNativeInputs = rs-harbor.lib.mkRustNativeBuildInputs {
|
||||
inherit pkgs;
|
||||
extra = [pkgs.pkg-config pkgs.cmake pkgs.ninja];
|
||||
};
|
||||
nativeLibraries = with pkgs; [
|
||||
fontconfig
|
||||
freetype
|
||||
libGL
|
||||
libdrm
|
||||
libglvnd
|
||||
libxkbcommon
|
||||
mesa
|
||||
wayland
|
||||
libx11
|
||||
libxcursor
|
||||
libxext
|
||||
libxfixes
|
||||
libxi
|
||||
libxrandr
|
||||
libxrender
|
||||
libxcb
|
||||
libxcb-util
|
||||
libxcb-image
|
||||
libxcb-keysyms
|
||||
libxcb-render-util
|
||||
libxcb-wm
|
||||
];
|
||||
runtimeLibraries = nativeLibraries;
|
||||
# Release archives are Ubuntu-built ELF binaries. Patch their
|
||||
# interpreter and shared-library references into the Nix store before
|
||||
# wrapping them, otherwise they fail before LD_LIBRARY_PATH is read.
|
||||
prebuiltDesktopRuntimeLibraries =
|
||||
runtimeLibraries
|
||||
++ [
|
||||
pkgs.zlib
|
||||
pkgs.stdenv.cc.cc.lib
|
||||
];
|
||||
prebuiltCliRuntimeLibraries = [pkgs.stdenv.cc.cc.lib];
|
||||
|
||||
source = pkgs.runCommand "openpencil-source-${version}" {} ''
|
||||
mkdir -p $out/vendor/agent $out/vendor/casement $out/vendor/jian
|
||||
cp -R --no-preserve=ownership ${./.}/. $out/
|
||||
rm -rf $out/vendor/agent $out/vendor/casement $out/vendor/jian
|
||||
cp -R --no-preserve=ownership ${agent}/. $out/vendor/agent
|
||||
cp -R --no-preserve=ownership ${casement}/. $out/vendor/casement
|
||||
cp -R --no-preserve=ownership ${jian}/. $out/vendor/jian
|
||||
'';
|
||||
|
||||
skiaBinaries = pkgs.fetchurl {
|
||||
url = "https://github.com/rust-skia/skia-binaries/releases/download/0.97.2/skia-binaries-da8fc6731fc439bc3b6a-x86_64-unknown-linux-gnu-gl-jpegd-jpege-pdf-textlayout.tar.gz";
|
||||
hash = "sha256-7nf70Bg+hU4pcnZwXk6GhYN8bH0DBEcslxRfzY9/LPw=";
|
||||
};
|
||||
skiaRepositoryHash = "da8fc6731fc439bc3b6aa90d63506a808f806b26";
|
||||
skiaGit = pkgs.writeShellScriptBin "git" ''
|
||||
if [ "$1" = rev-parse ] && [ "$2" = --short=20 ] && [ "$3" = HEAD ]; then
|
||||
printf '%s\n' ${skiaRepositoryHash}
|
||||
exit 0
|
||||
fi
|
||||
exec ${pkgs.git}/bin/git "$@"
|
||||
'';
|
||||
nativeEnv = {
|
||||
SKIA_BINARIES_URL = "file://${skiaBinaries}";
|
||||
FORCE_SKIA_BINARIES_DOWNLOAD = "1";
|
||||
};
|
||||
commonArgs = {
|
||||
inherit version;
|
||||
src = ./.;
|
||||
cargoLock = ./Cargo.lock;
|
||||
strictDeps = true;
|
||||
cargoArtifacts = null;
|
||||
doCheck = false;
|
||||
env = nativeEnv;
|
||||
nativeBuildInputs = rustNativeInputs ++ [pkgs.makeWrapper pkgs.python3 pkgs.perl skiaGit];
|
||||
buildInputs = nativeLibraries;
|
||||
rsHarborCargoTomlContents = builtins.readFile ./Cargo.toml;
|
||||
preBuild = ''
|
||||
# skia-bindings uses the packaged rust-skia commit to select its
|
||||
# prebuilt archive. Recreate that metadata in the Nix source tree
|
||||
# because Cargo vendor sources do not retain the upstream Git repo.
|
||||
mkdir -p .git/refs/heads
|
||||
printf 'ref: refs/heads/main\n' > .git/HEAD
|
||||
printf '%s\n' ${skiaRepositoryHash} > .git/refs/heads/main
|
||||
mkdir -p vendor
|
||||
rm -rf vendor/agent vendor/casement vendor/jian
|
||||
cp -R --no-preserve=ownership ${agent} vendor/agent
|
||||
cp -R --no-preserve=ownership ${casement} vendor/casement
|
||||
cp -R --no-preserve=ownership ${jian} vendor/jian
|
||||
export SKIA_BINARIES_URL=${lib.escapeShellArg nativeEnv.SKIA_BINARIES_URL}
|
||||
export FORCE_SKIA_BINARIES_DOWNLOAD=1
|
||||
'';
|
||||
};
|
||||
|
||||
nativePackage = craneLib.buildPackage (commonArgs
|
||||
// {
|
||||
pname = "openpencil-native";
|
||||
cargoBuildCommand = "cargo build --release --package op-host-desktop --package op-cli --package op-host-web-server";
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
install -Dm755 target/release/openpencil-desktop $out/bin/openpencil-desktop
|
||||
install -Dm755 target/release/op $out/bin/op
|
||||
install -Dm755 target/release/op-host-web-server $out/bin/op-host-web-server
|
||||
install -Dm644 crates/op-host-desktop/assets/icon.png $out/share/icons/hicolor/1024x1024/apps/openpencil.png
|
||||
install -Dm644 ${pkgs.writeText "openpencil.desktop" ''
|
||||
[Desktop Entry]
|
||||
Name=OpenPencil
|
||||
Comment=Open-source AI-native vector design tool
|
||||
Exec=openpencil-desktop %U
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Icon=openpencil
|
||||
Categories=Graphics;
|
||||
MimeType=application/x-openpencil;
|
||||
''} $out/share/applications/openpencil.desktop
|
||||
install -Dm644 ${pkgs.writeText "openpencil.xml" ''
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
|
||||
<mime-type type="application/x-openpencil">
|
||||
<comment>OpenPencil Document</comment>
|
||||
<glob pattern="*.op"/>
|
||||
<glob pattern="*.pen"/>
|
||||
</mime-type>
|
||||
</mime-info>
|
||||
''} $out/share/mime/packages/openpencil.xml
|
||||
install -Dm644 crates/op-host-desktop/assets/icon.png $out/share/pixmaps/openpencil.png
|
||||
runHook postInstall
|
||||
'';
|
||||
postFixup = ''
|
||||
for program in openpencil-desktop op op-host-web-server; do
|
||||
wrapProgram "$out/bin/$program" \
|
||||
--prefix LD_LIBRARY_PATH : ${lib.makeLibraryPath runtimeLibraries}
|
||||
done
|
||||
'';
|
||||
});
|
||||
|
||||
runtimePackage = pkgs.symlinkJoin {
|
||||
name = "openpencil-runtime-${version}";
|
||||
paths = [nativePackage];
|
||||
postBuild = ''
|
||||
install -Dm644 ${defaultDocument} "$out/share/openpencil/default.op"
|
||||
'';
|
||||
};
|
||||
|
||||
# Upstream publishes matching Linux archives for tagged releases. These
|
||||
# outputs are deliberately separate from the source-built packages: the
|
||||
# source build remains the default, while users with a matching release
|
||||
# asset can avoid compiling Rust and Skia.
|
||||
prebuiltDesktopPackage = pkgs.stdenvNoCC.mkDerivation {
|
||||
pname = "openpencil-prebuilt";
|
||||
version = releaseVersion;
|
||||
src = pkgs.fetchurl {
|
||||
url = "https://github.com/ZSeven-W/openpencil/releases/download/v${releaseVersion}/openpencil-desktop-linux-x86_64.tar.gz";
|
||||
hash = releaseManifest.desktopHash;
|
||||
};
|
||||
dontUnpack = true;
|
||||
nativeBuildInputs = [pkgs.autoPatchelfHook pkgs.makeWrapper];
|
||||
buildInputs = prebuiltDesktopRuntimeLibraries;
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
tar -xzf "$src" -C "$TMPDIR"
|
||||
install -Dm755 "$TMPDIR/openpencil-desktop" $out/bin/openpencil-desktop
|
||||
install -Dm644 ${./crates/op-host-desktop/assets/icon.png} \
|
||||
$out/share/icons/hicolor/1024x1024/apps/openpencil.png
|
||||
install -Dm644 ${pkgs.writeText "openpencil-prebuilt.desktop" ''
|
||||
[Desktop Entry]
|
||||
Name=OpenPencil
|
||||
Comment=Open-source AI-native vector design tool
|
||||
Exec=openpencil-desktop %U
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Icon=openpencil
|
||||
Categories=Graphics;
|
||||
''} $out/share/applications/openpencil.desktop
|
||||
runHook postInstall
|
||||
'';
|
||||
postFixup = ''
|
||||
wrapProgram $out/bin/openpencil-desktop \
|
||||
--prefix LD_LIBRARY_PATH : ${lib.makeLibraryPath prebuiltDesktopRuntimeLibraries}
|
||||
'';
|
||||
};
|
||||
|
||||
prebuiltCliPackage = pkgs.stdenvNoCC.mkDerivation {
|
||||
pname = "openpencil-cli-prebuilt";
|
||||
version = releaseVersion;
|
||||
src = pkgs.fetchurl {
|
||||
url = "https://github.com/ZSeven-W/openpencil/releases/download/v${releaseVersion}/op-cli-linux-x86_64.tar.gz";
|
||||
hash = releaseManifest.cliHash;
|
||||
};
|
||||
dontUnpack = true;
|
||||
nativeBuildInputs = [pkgs.autoPatchelfHook];
|
||||
buildInputs = prebuiltCliRuntimeLibraries;
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
tar -xzf "$src" -C "$TMPDIR"
|
||||
install -Dm755 "$TMPDIR/op" $out/bin/op
|
||||
runHook postInstall
|
||||
'';
|
||||
};
|
||||
|
||||
wasmBindgen = rs-harbor.lib.resolveWasmBindgenCli {
|
||||
inherit lib pkgs;
|
||||
cargoLock = ./Cargo.lock;
|
||||
};
|
||||
|
||||
wasmGate = rawName: optimizedName: ''
|
||||
wasm_opt_help="$(${pkgs.binaryen}/bin/wasm-opt --help 2>&1 || true)"
|
||||
wasm_opt_flags=""
|
||||
for flag in --enable-bulk-memory --enable-bulk-memory-opt --enable-nontrapping-float-to-int; do
|
||||
if printf '%s\n' "$wasm_opt_help" | grep -qF -- "$flag"; then
|
||||
wasm_opt_flags="$wasm_opt_flags $flag"
|
||||
fi
|
||||
done
|
||||
${pkgs.binaryen}/bin/wasm-opt $wasm_opt_flags -Oz "$out/${rawName}" -o "$out/${optimizedName}"
|
||||
cp "$out/${optimizedName}" "$out/${rawName}"
|
||||
env_count="$(${pkgs.nodejs}/bin/node -e '
|
||||
const fs = require("fs");
|
||||
const buf = fs.readFileSync(process.argv[1]);
|
||||
WebAssembly.compile(buf).then(mod => {
|
||||
console.log(WebAssembly.Module.imports(mod).filter(i => i.module === "env").length);
|
||||
}).catch(e => { console.error(e); process.exit(1); });
|
||||
' "$out/${rawName}")"
|
||||
test "$env_count" = 0
|
||||
gzip_bytes="$(${pkgs.gzip}/bin/gzip -c "$out/${rawName}" | ${pkgs.coreutils}/bin/wc -c)"
|
||||
test "$gzip_bytes" -le 6291456
|
||||
'';
|
||||
|
||||
webHostBundle = craneLib.buildPackage (commonArgs
|
||||
// {
|
||||
pname = "openpencil-web-bundle";
|
||||
cargoBuildCommand = "cargo build --release --target wasm32-unknown-unknown --no-default-features --features canvaskit --package op-host-web";
|
||||
nativeBuildInputs = commonArgs.nativeBuildInputs ++ [wasmBindgen.package pkgs.binaryen pkgs.nodejs pkgs.gzip];
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
mkdir -p $out
|
||||
${wasmBindgen.package}/bin/wasm-bindgen --target web --out-dir $out target/wasm32-unknown-unknown/release/op_host_web.wasm
|
||||
cp -R crates/op-host-web/assets/canvaskit $out/canvaskit
|
||||
${wasmGate "op_host_web_bg.wasm" "op_host_web_bg.opt.wasm"}
|
||||
runHook postInstall
|
||||
'';
|
||||
});
|
||||
|
||||
webSdkWasm = craneLib.buildPackage (commonArgs
|
||||
// {
|
||||
pname = "openpencil-web-sdk-wasm";
|
||||
cargoBuildCommand = "cargo build --release --target wasm32-unknown-unknown --features canvaskit --package op-web-sdk";
|
||||
nativeBuildInputs = commonArgs.nativeBuildInputs ++ [wasmBindgen.package pkgs.binaryen pkgs.nodejs pkgs.gzip];
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
mkdir -p $out
|
||||
${wasmBindgen.package}/bin/wasm-bindgen --target web --out-dir $out target/wasm32-unknown-unknown/release/op_web_sdk.wasm
|
||||
${wasmGate "op_web_sdk_bg.wasm" "op_web_sdk_bg.opt.wasm"}
|
||||
runHook postInstall
|
||||
'';
|
||||
});
|
||||
|
||||
webServerPackage = pkgs.symlinkJoin {
|
||||
name = "openpencil-${version}";
|
||||
paths = [nativePackage];
|
||||
postBuild = ''
|
||||
mkdir -p $out/bin/web-bundle
|
||||
cp -R ${webHostBundle}/. $out/bin/web-bundle/
|
||||
'';
|
||||
};
|
||||
opCliPackage = pkgs.symlinkJoin {
|
||||
name = "openpencil-cli-${version}";
|
||||
paths = [nativePackage];
|
||||
postBuild = ''
|
||||
mkdir -p $out/bin
|
||||
ln -sf ${nativePackage}/bin/op $out/bin/op
|
||||
'';
|
||||
};
|
||||
|
||||
defaultDocument = pkgs.writeText "openpencil-default-${version}.op" ''
|
||||
{
|
||||
"version": "${version}",
|
||||
"name": "OpenPencil Nix Session",
|
||||
"children": []
|
||||
}
|
||||
'';
|
||||
|
||||
bunToolchain = js-harbor.lib.mkBunToolchain {
|
||||
inherit pkgs;
|
||||
packageJson = ./packages/package.json;
|
||||
};
|
||||
bunDeps = js-harbor.lib.mkBunWorkspaceDeps {
|
||||
inherit pkgs;
|
||||
bun = bunToolchain.bun;
|
||||
src = source;
|
||||
packageJson = ./packages/package.json;
|
||||
lockfile = ./packages/bun.lock;
|
||||
version = "1.3.14";
|
||||
installFlags = ["--cwd" "packages"];
|
||||
# Bun's hoisted aliases are order-dependent when multiple versions
|
||||
# are present; nested consumers already point at the correct ones.
|
||||
postInstallNormalize = ''
|
||||
rm -f packages/node_modules/.bun/node_modules/{string-width,strip-ansi,wrap-ansi}
|
||||
'';
|
||||
hash = "sha256-jaGVEagUeYrM2MzjUPvvzIF7DJwafLXJiWLWtts38SI=";
|
||||
};
|
||||
webSdkPackages = pkgs.stdenvNoCC.mkDerivation {
|
||||
pname = "openpencil-web-sdk-packages";
|
||||
inherit version;
|
||||
src = source;
|
||||
nativeBuildInputs = [bunToolchain.bun pkgs.coreutils pkgs.nodejs];
|
||||
buildPhase = ''
|
||||
runHook preBuild
|
||||
rm -rf packages/node_modules packages/op-web-sdk/wasm
|
||||
cp -R ${bunDeps}/packages/node_modules packages/node_modules
|
||||
for workspace in op-web-sdk op-web-sdk-react op-web-sdk-vue; do
|
||||
cp -R ${bunDeps}/packages/$workspace/node_modules packages/$workspace/
|
||||
done
|
||||
chmod -R u+w packages/node_modules
|
||||
chmod -R u+w packages/op-web-sdk/node_modules packages/op-web-sdk-react/node_modules packages/op-web-sdk-vue/node_modules
|
||||
patchShebangs packages/node_modules
|
||||
find packages/node_modules -type f -exec sed -i \
|
||||
-e 's|^#!/usr/bin/env bun$|#!${bunToolchain.bun}/bin/bun|' \
|
||||
-e 's|^#!/usr/bin/env node$|#!${pkgs.nodejs}/bin/node|' {} +
|
||||
mkdir -p packages/op-web-sdk/wasm
|
||||
cp -R ${webSdkWasm}/. packages/op-web-sdk/wasm/
|
||||
bun run --cwd packages sync-version:check
|
||||
bun run --cwd packages lint
|
||||
bun run --cwd packages/op-web-sdk typecheck
|
||||
bun run --cwd packages/op-web-sdk test
|
||||
bun run --cwd packages/op-web-sdk build
|
||||
bun run --cwd packages/op-web-sdk-react typecheck
|
||||
bun run --cwd packages/op-web-sdk-react test
|
||||
bun run --cwd packages/op-web-sdk-react build
|
||||
bun run --cwd packages/op-web-sdk-vue typecheck
|
||||
bun run --cwd packages/op-web-sdk-vue test
|
||||
bun run --cwd packages/op-web-sdk-vue build
|
||||
runHook postBuild
|
||||
'';
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
mkdir -p $out
|
||||
(cd packages/op-web-sdk && bun pm pack --destination $out)
|
||||
(cd packages/op-web-sdk-react && bun pm pack --destination $out)
|
||||
(cd packages/op-web-sdk-vue && bun pm pack --destination $out)
|
||||
runHook postInstall
|
||||
'';
|
||||
};
|
||||
|
||||
pythonEnv = py-harbor.lib.mkPythonEnv {inherit pkgs;};
|
||||
pythonCheck =
|
||||
pkgs.runCommand "openpencil-python-check" {
|
||||
nativeBuildInputs = [pythonEnv];
|
||||
} ''
|
||||
cp -R ${source} "$TMPDIR/openpencil-source"
|
||||
chmod -R u+w "$TMPDIR/openpencil-source"
|
||||
cd "$TMPDIR/openpencil-source"
|
||||
python -m compileall -q scripts tools
|
||||
touch $out
|
||||
'';
|
||||
|
||||
# Exercise the same `nix run` surface that users consume, inside a
|
||||
# clean NixOS VM rather than only evaluating the derivations.
|
||||
prebuiltTestFlake = pkgs.writeTextDir "flake.nix" ''
|
||||
{
|
||||
outputs = {self}: {
|
||||
apps.x86_64-linux.prebuilt = {
|
||||
type = "app";
|
||||
program = "${prebuiltDesktopPackage}/bin/openpencil-desktop";
|
||||
};
|
||||
apps.x86_64-linux.prebuilt-cli = {
|
||||
type = "app";
|
||||
program = "${prebuiltCliPackage}/bin/op";
|
||||
};
|
||||
};
|
||||
}
|
||||
'';
|
||||
|
||||
prebuiltRuntimeTest = pkgs.testers.runNixOSTest ({...}: {
|
||||
name = "openpencil-prebuilt-runtime";
|
||||
nodes.machine = {pkgs, ...}: {
|
||||
system.stateVersion = "25.11";
|
||||
virtualisation.memorySize = 2048;
|
||||
nix.settings.experimental-features = ["nix-command" "flakes"];
|
||||
environment.etc."openpencil-test-flake".source = prebuiltTestFlake;
|
||||
environment.variables = {
|
||||
LIBGL_ALWAYS_SOFTWARE = "1";
|
||||
MESA_LOADER_DRIVER_OVERRIDE = "llvmpipe";
|
||||
GALLIUM_DRIVER = "llvmpipe";
|
||||
};
|
||||
environment.systemPackages = [
|
||||
prebuiltDesktopPackage
|
||||
prebuiltCliPackage
|
||||
pkgs.xvfb-run
|
||||
];
|
||||
};
|
||||
testScript = ''
|
||||
machine.succeed("nix run --offline /etc/openpencil-test-flake#prebuilt-cli -- --version | grep -F ${releaseVersion}")
|
||||
machine.succeed("set +e; timeout 15s xvfb-run -a -s '-screen 0 1280x720x24' nix run --offline /etc/openpencil-test-flake#prebuilt >/tmp/openpencil.log 2>&1; rc=$?; test $rc -eq 0 -o $rc -eq 124; ! grep -E 'error while loading|cannot open shared object' /tmp/openpencil.log")
|
||||
'';
|
||||
});
|
||||
|
||||
runtimePrebuiltPackage = pkgs.symlinkJoin {
|
||||
name = "openpencil-runtime-prebuilt-${version}";
|
||||
paths = [prebuiltDesktopPackage prebuiltCliPackage];
|
||||
nativeBuildInputs = [pkgs.makeWrapper];
|
||||
postBuild = ''
|
||||
rm -f "$out/bin/op"
|
||||
makeWrapper ${prebuiltCliPackage}/bin/op "$out/bin/op" \
|
||||
--set-default OPENPENCIL_DESKTOP_BIN "$out/bin/openpencil-desktop"
|
||||
install -Dm644 ${defaultDocument} "$out/share/openpencil/default.op"
|
||||
'';
|
||||
};
|
||||
|
||||
runtimePrebuiltToolsCheck = pkgs.runCommand "openpencil-runtime-prebuilt-tools-${version}" {} ''
|
||||
test -x ${runtimePrebuiltPackage}/bin/openpencil-desktop
|
||||
test -x ${runtimePrebuiltPackage}/bin/op
|
||||
touch "$out"
|
||||
'';
|
||||
|
||||
webServerToolsCheck = pkgs.runCommand "openpencil-web-server-tools-${version}" {} ''
|
||||
test -x ${webServerPackage}/bin/op-host-web-server
|
||||
test -d ${webServerPackage}/bin/web-bundle
|
||||
touch "$out"
|
||||
'';
|
||||
|
||||
skillBundle = builtins.fromJSON (
|
||||
builtins.replaceStrings
|
||||
["__OPENPENCIL_VERSION__"]
|
||||
[version]
|
||||
(builtins.readFile ./crates/op-cli/assets/skill-bundle.json)
|
||||
);
|
||||
skillBundleFiles =
|
||||
builtins.mapAttrs
|
||||
(relativePath: contents:
|
||||
pkgs.writeText
|
||||
"openpencil-skill-${builtins.replaceStrings ["/"] ["-"] relativePath}"
|
||||
contents)
|
||||
skillBundle.files;
|
||||
skillsPackage = pkgs.runCommand "openpencil-skills-${version}" {} ''
|
||||
mkdir -p "$out/share/skillnet/openpencil"
|
||||
install -Dm644 ${./nix/integration/Skillnet.pkl} \
|
||||
"$out/share/skillnet/openpencil/Skillnet.pkl"
|
||||
${lib.concatStringsSep "\n" (
|
||||
lib.mapAttrsToList
|
||||
(relativePath: source: "install -Dm644 ${source} \"$out/share/skillnet/openpencil/${relativePath}\"")
|
||||
skillBundleFiles
|
||||
)}
|
||||
'';
|
||||
|
||||
appimage = rs-harbor.lib.mkAppImage {
|
||||
inherit nix-appimage system version;
|
||||
pname = "openpencil";
|
||||
program = "${nativePackage}/bin/openpencil-desktop";
|
||||
};
|
||||
in {
|
||||
packages = {
|
||||
default = webServerPackage;
|
||||
openpencil = webServerPackage;
|
||||
op-cli = opCliPackage;
|
||||
runtime = runtimePackage;
|
||||
runtime-prebuilt = runtimePrebuiltPackage;
|
||||
prebuilt = prebuiltDesktopPackage;
|
||||
prebuilt-cli = prebuiltCliPackage;
|
||||
skills = skillsPackage;
|
||||
web-server = webServerPackage;
|
||||
web-bundle = webHostBundle;
|
||||
web-sdk-wasm = webSdkWasm;
|
||||
web-sdk-packages = webSdkPackages;
|
||||
appimage = appimage;
|
||||
python-tools = pythonCheck;
|
||||
};
|
||||
|
||||
apps = {
|
||||
default = {
|
||||
type = "app";
|
||||
program = "${nativePackage}/bin/openpencil-desktop";
|
||||
};
|
||||
op = {
|
||||
type = "app";
|
||||
program = "${nativePackage}/bin/op";
|
||||
};
|
||||
prebuilt = {
|
||||
type = "app";
|
||||
program = "${prebuiltDesktopPackage}/bin/openpencil-desktop";
|
||||
};
|
||||
prebuilt-cli = {
|
||||
type = "app";
|
||||
program = "${prebuiltCliPackage}/bin/op";
|
||||
};
|
||||
web-server = {
|
||||
type = "app";
|
||||
program = "${webServerPackage}/bin/op-host-web-server";
|
||||
};
|
||||
integration-export = {
|
||||
type = "app";
|
||||
program = "${pkgs.writeShellApplication {
|
||||
name = "openpencil-integration-export";
|
||||
runtimeInputs = [nix-pklx.packages.${system}.pklx pkgs.coreutils];
|
||||
text = ''
|
||||
export SSL_CERT_FILE="${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"
|
||||
output="''${1:-nix/integration/openpencil.nix}"
|
||||
tmp="$(mktemp)"
|
||||
trap 'rm -f "$tmp"' EXIT
|
||||
pklx eval nix/integration/OpenPencil.pkl -o "$tmp"
|
||||
mv "$tmp" "$output"
|
||||
echo "Wrote $output from nix/integration/OpenPencil.pkl"
|
||||
'';
|
||||
}}/bin/openpencil-integration-export";
|
||||
};
|
||||
};
|
||||
|
||||
devShells.default = pkgs.mkShell {
|
||||
packages = [
|
||||
toolchain.rustToolchain
|
||||
bunToolchain.bun
|
||||
pythonEnv
|
||||
wasmBindgen.package
|
||||
pkgs.binaryen
|
||||
pkgs.nodejs
|
||||
pkgs.gzip
|
||||
pkgs.cargo-watch
|
||||
pkgs.chromium
|
||||
];
|
||||
nativeBuildInputs = rustNativeInputs;
|
||||
buildInputs = nativeLibraries;
|
||||
env = nativeEnv;
|
||||
};
|
||||
|
||||
formatter = pkgs.alejandra;
|
||||
|
||||
checks = {
|
||||
default = webServerPackage;
|
||||
native = nativePackage;
|
||||
web-bundle = webHostBundle;
|
||||
web-sdk-wasm = webSdkWasm;
|
||||
web-sdk-packages = webSdkPackages;
|
||||
python = pythonCheck;
|
||||
prebuilt = prebuiltDesktopPackage;
|
||||
prebuilt-cli = prebuiltCliPackage;
|
||||
prebuilt-runtime = prebuiltRuntimeTest;
|
||||
prebuilt-runtime-tools = runtimePrebuiltToolsCheck;
|
||||
web-server-tools = webServerToolsCheck;
|
||||
integration-manifest =
|
||||
pkgs.runCommand "openpencil-integration-manifest" {
|
||||
nativeBuildInputs = [nix-pklx.packages.${system}.pklx];
|
||||
SSL_CERT_FILE = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
|
||||
} ''
|
||||
pklx eval ${./nix/integration/OpenPencil.pkl} -o "$out"
|
||||
'';
|
||||
integration-sidecar =
|
||||
pkgs.runCommand "openpencil-integration-sidecar" {
|
||||
nativeBuildInputs = [nix-pklx.packages.${system}.pklx pkgs.diffutils];
|
||||
SSL_CERT_FILE = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
|
||||
} ''
|
||||
generated="$TMPDIR/openpencil.nix"
|
||||
pklx eval ${./nix/integration/OpenPencil.pkl} -o "$generated"
|
||||
diff -u ${./nix/integration/openpencil.nix} "$generated"
|
||||
touch "$out"
|
||||
'';
|
||||
flake-format = pkgs.runCommand "openpencil-flake-format" {} ''
|
||||
${pkgs.alejandra}/bin/alejandra --check ${./flake.nix}
|
||||
touch $out
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
118
nix/integration/OpenPencil.pkl
Normal file
118
nix/integration/OpenPencil.pkl
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
/// Stable integration contract exported by the OpenPencil flake.
|
||||
|
||||
class PackageOutputs {
|
||||
sourceRuntime: String
|
||||
prebuiltRuntime: String
|
||||
webServer: String
|
||||
skills: String
|
||||
}
|
||||
|
||||
class Executables {
|
||||
desktop: String
|
||||
cli: String
|
||||
}
|
||||
|
||||
class McpTransport {
|
||||
kind: String
|
||||
args: Listing<String> = new Listing {}
|
||||
urlTemplate: String? = null
|
||||
launcherArgs: Listing<String> = new Listing {}
|
||||
loopbackOnly: Boolean = true
|
||||
}
|
||||
|
||||
class HarnessAdapter {
|
||||
format: String
|
||||
configPath: String
|
||||
serverKey: String
|
||||
}
|
||||
|
||||
class Integration {
|
||||
schemaVersion: Int
|
||||
id: String
|
||||
systems: Listing<String>
|
||||
packages: PackageOutputs
|
||||
executables: Executables
|
||||
documentTemplate: String
|
||||
mcp: Mapping<String, McpTransport>
|
||||
harnesses: Mapping<String, HarnessAdapter>
|
||||
skillnetManifest: String
|
||||
skills: Listing<String>
|
||||
}
|
||||
|
||||
integration = new Integration {
|
||||
schemaVersion = 1
|
||||
id = "openpencil"
|
||||
systems = new Listing { "x86_64-linux" }
|
||||
packages = new PackageOutputs {
|
||||
sourceRuntime = "runtime"
|
||||
prebuiltRuntime = "runtime-prebuilt"
|
||||
webServer = "web-server"
|
||||
skills = "skills"
|
||||
}
|
||||
executables = new Executables {
|
||||
desktop = "openpencil-desktop"
|
||||
cli = "op"
|
||||
}
|
||||
documentTemplate = "share/openpencil/default.op"
|
||||
mcp = new {
|
||||
["stdio"] = new McpTransport {
|
||||
kind = "stdio"
|
||||
args = new Listing { "--mcp"; "{document}" }
|
||||
}
|
||||
["http"] = new McpTransport {
|
||||
kind = "http"
|
||||
urlTemplate = "http://127.0.0.1:{port}/mcp"
|
||||
launcherArgs = new Listing { "--live-mcp"; "{port}" }
|
||||
loopbackOnly = true
|
||||
}
|
||||
}
|
||||
harnesses = new {
|
||||
["claude"] = new HarnessAdapter {
|
||||
format = "json"
|
||||
configPath = "~/.claude.json"
|
||||
serverKey = "openpencil"
|
||||
}
|
||||
["codex"] = new HarnessAdapter {
|
||||
format = "toml"
|
||||
configPath = "~/.codex/config.toml"
|
||||
serverKey = "openpencil"
|
||||
}
|
||||
["gemini"] = new HarnessAdapter {
|
||||
format = "json"
|
||||
configPath = "~/.gemini/settings.json"
|
||||
serverKey = "openpencil"
|
||||
}
|
||||
["opencode"] = new HarnessAdapter {
|
||||
format = "json"
|
||||
configPath = "~/.opencode/config.json"
|
||||
serverKey = "openpencil"
|
||||
}
|
||||
["kiro"] = new HarnessAdapter {
|
||||
format = "json"
|
||||
configPath = "~/.kiro/settings.json"
|
||||
serverKey = "openpencil"
|
||||
}
|
||||
["copilot"] = new HarnessAdapter {
|
||||
format = "json"
|
||||
configPath = "~/.config/github-copilot/mcp.json"
|
||||
serverKey = "openpencil"
|
||||
}
|
||||
["antigravity"] = new HarnessAdapter {
|
||||
format = "json"
|
||||
configPath = "~/.gemini/config/mcp_config.json"
|
||||
serverKey = "openpencil"
|
||||
}
|
||||
["grok-build"] = new HarnessAdapter {
|
||||
format = "toml"
|
||||
configPath = "~/.grok/config.toml"
|
||||
serverKey = "openpencil"
|
||||
}
|
||||
["hermes"] = new HarnessAdapter {
|
||||
format = "nix"
|
||||
configPath = "services.infernix.hermes-agent.mcpServers"
|
||||
serverKey = "openpencil"
|
||||
}
|
||||
}
|
||||
skillnetManifest = "share/skillnet/openpencil/Skillnet.pkl"
|
||||
skills = new Listing { "openpencil-design" }
|
||||
}
|
||||
21
nix/integration/README.md
Normal file
21
nix/integration/README.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# OpenPencil Nix integration surface
|
||||
|
||||
The OpenPencil flake exports a versioned Pkl contract at
|
||||
`nix/integration/OpenPencil.pkl` and the generated Nix sidecar at
|
||||
`nix/integration/openpencil.nix` (`lib.integrationManifest`). Downstream flakes
|
||||
should consume the sidecar rather than duplicating executable names, MCP
|
||||
transport arguments, or harness config paths.
|
||||
|
||||
For each supported system the contract names these package outputs:
|
||||
|
||||
- `runtime`: source-built desktop and CLI runtime;
|
||||
- `runtime-prebuilt`: the matching release binaries, wrapped so `op` can find
|
||||
the desktop executable (exports both `openpencil-desktop` and `op`);
|
||||
- `web-server`: the source-built `op-host-web-server` plus its web bundle;
|
||||
- `skills`: an immutable Skillnet bundle containing `Skillnet.pkl` and the
|
||||
OpenPencil design skill.
|
||||
|
||||
The default MCP adapter is per-session stdio (`--mcp <document>`). The HTTP
|
||||
adapter is loopback-only and is intended for an explicitly enabled live-canvas
|
||||
service (`--live-mcp <port>`). Consumers should merge the generated adapter
|
||||
entry into their harness config atomically and preserve unrelated keys.
|
||||
15
nix/integration/Skillnet.pkl
Normal file
15
nix/integration/Skillnet.pkl
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
/// Skillnet manifest shipped in the immutable OpenPencil skills bundle.
|
||||
|
||||
class Skill {
|
||||
role: String = "entrypoint"
|
||||
dependencies: Listing<String> = new Listing {}
|
||||
source: String? = null
|
||||
}
|
||||
|
||||
schemaVersion = 1
|
||||
skills: Mapping<String, Skill> = new {
|
||||
["openpencil-design"] = new Skill {
|
||||
role = "entrypoint"
|
||||
source = "skills/openpencil-design"
|
||||
}
|
||||
}
|
||||
11
nix/integration/openpencil.nix
Normal file
11
nix/integration/openpencil.nix
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{ integration = { __pkl_class = "Integration"; schemaVersion = 1; id = "openpencil"; systems = [
|
||||
"x86_64-linux"
|
||||
]; packages = { __pkl_class = "PackageOutputs"; sourceRuntime = "runtime"; prebuiltRuntime = "runtime-prebuilt"; webServer = "web-server"; skills = "skills"; }; executables = { __pkl_class = "Executables"; desktop = "openpencil-desktop"; cli = "op"; }; documentTemplate = "share/openpencil/default.op"; mcp = { stdio = { __pkl_class = "McpTransport"; kind = "stdio"; args = [
|
||||
"--mcp"
|
||||
"{document}"
|
||||
]; urlTemplate = null; launcherArgs = [ ]; loopbackOnly = true; }; http = { __pkl_class = "McpTransport"; kind = "http"; args = [ ]; urlTemplate = "http://127.0.0.1:{port}/mcp"; launcherArgs = [
|
||||
"--live-mcp"
|
||||
"{port}"
|
||||
]; loopbackOnly = true; }; }; harnesses = { claude = { __pkl_class = "HarnessAdapter"; format = "json"; configPath = "~/.claude.json"; serverKey = "openpencil"; }; codex = { __pkl_class = "HarnessAdapter"; format = "toml"; configPath = "~/.codex/config.toml"; serverKey = "openpencil"; }; gemini = { __pkl_class = "HarnessAdapter"; format = "json"; configPath = "~/.gemini/settings.json"; serverKey = "openpencil"; }; opencode = { __pkl_class = "HarnessAdapter"; format = "json"; configPath = "~/.opencode/config.json"; serverKey = "openpencil"; }; kiro = { __pkl_class = "HarnessAdapter"; format = "json"; configPath = "~/.kiro/settings.json"; serverKey = "openpencil"; }; copilot = { __pkl_class = "HarnessAdapter"; format = "json"; configPath = "~/.config/github-copilot/mcp.json"; serverKey = "openpencil"; }; antigravity = { __pkl_class = "HarnessAdapter"; format = "json"; configPath = "~/.gemini/config/mcp_config.json"; serverKey = "openpencil"; }; grok-build = { __pkl_class = "HarnessAdapter"; format = "toml"; configPath = "~/.grok/config.toml"; serverKey = "openpencil"; }; hermes = { __pkl_class = "HarnessAdapter"; format = "nix"; configPath = "services.infernix.hermes-agent.mcpServers"; serverKey = "openpencil"; }; }; skillnetManifest = "share/skillnet/openpencil/Skillnet.pkl"; skills = [
|
||||
"openpencil-design"
|
||||
]; }; }
|
||||
5
nix/release-manifest.json
Normal file
5
nix/release-manifest.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"version": "0.8.2",
|
||||
"desktopHash": "sha256-btwzBgYRuaKkMqRxbz0uDpJTfnaYqWXl16WA82sCyCI=",
|
||||
"cliHash": "sha256-rv+xEUhX57gQ5mzZ7JJ/qIPd4Ms+vwpu4miR4oiNIKI="
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@
|
|||
"op-web-sdk-vue"
|
||||
],
|
||||
"type": "module",
|
||||
"packageManager": "bun@1.3.14",
|
||||
"scripts": {
|
||||
"lint": "oxlint .",
|
||||
"lint:fix": "oxlint --fix .",
|
||||
|
|
|
|||
Loading…
Reference in a new issue