mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-14 13:22:37 +00:00
Add support for Nix and NixOS (#130)
* fix: add missing resolved/integrity fields to package-lock.json npm omits resolved URLs and integrity hashes for workspace-local node_modules overrides. This breaks offline installers like Nix's npm ci. Add the missing fields for 25 workspace-hoisted packages. * feat: add Nix flake with package and NixOS module Add a Nix flake that builds the Paseo daemon (server + CLI) and provides a NixOS module for declarative deployment. Package (nix/package.nix): - Builds relay, server, and CLI workspaces - Skips onnxruntime-node install script (sandbox-incompatible) - Rebuilds only node-pty for native terminal support - Source filter excludes app/website/desktop workspaces NixOS module (nix/module.nix): - Systemd service with configurable user, port, listen address - allowedHosts for DNS rebinding protection - relay.enable to toggle remote access via app.paseo.sh - inheritUserEnvironment to expose user tools (git, ssh) to agents - openFirewall and extra environment variables ci: add Nix hash maintenance scripts and workflows scripts/fix-lockfile.mjs: Adds missing resolved/integrity fields to package-lock.json for workspace-local overrides. Idempotent, uses `npm view`. scripts/update-nix.sh: Runs fix-lockfile.mjs, prefetches deps, computes NAR hash, and updates npmDepsHash in nix/package.nix. Supports --check for CI. .github/workflows/nix-build.yml: Builds the Nix package on push/PR and verifies the lockfile and hash are up to date. .github/workflows/fix-nix-hash.yml: Auto-fixes lockfile signatures and Nix hash on dependabot PRs. fix: update npmDepsHash after upstream sync nix: allowlist workspace symlinks instead of blocklist Prevents build failures when upstream adds new workspace packages. * don't block PRs on nix failures * better document npm workaround * fix hash update script, and update hash * integrate with npm run build:daemon * ci: trigger nix build on highlight changes * fix(nix): update npmDepsHash --------- Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
This commit is contained in:
co-authored by
Mohamed Boudra
parent
765a11c7ab
commit
bb560809c7
@@ -0,0 +1,41 @@
|
|||||||
|
name: Fix Nix hash
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- 'package.json'
|
||||||
|
- 'package-lock.json'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
fix-nix-hash:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.actor == 'dependabot[bot]'
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
ref: ${{ github.head_ref }}
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '22'
|
||||||
|
cache: 'npm'
|
||||||
|
|
||||||
|
- uses: cachix/install-nix-action@v31
|
||||||
|
with:
|
||||||
|
nix_path: nixpkgs=channel:nixos-unstable
|
||||||
|
|
||||||
|
- name: Fix lockfile and update hash
|
||||||
|
run: ./scripts/update-nix.sh
|
||||||
|
|
||||||
|
- name: Commit changes
|
||||||
|
run: |
|
||||||
|
git diff --quiet package-lock.json nix/package.nix && exit 0
|
||||||
|
git config user.name "github-actions[bot]"
|
||||||
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
|
git add package-lock.json nix/package.nix
|
||||||
|
git commit -m "fix: update lockfile signatures and Nix hash"
|
||||||
|
git push
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
name: Nix Build
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
paths:
|
||||||
|
- 'nix/**'
|
||||||
|
- 'flake.nix'
|
||||||
|
- 'flake.lock'
|
||||||
|
- 'package.json'
|
||||||
|
- 'package-lock.json'
|
||||||
|
- 'packages/highlight/**'
|
||||||
|
- 'packages/server/**'
|
||||||
|
- 'packages/relay/**'
|
||||||
|
- 'packages/cli/**'
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
paths:
|
||||||
|
- 'nix/**'
|
||||||
|
- 'flake.nix'
|
||||||
|
- 'flake.lock'
|
||||||
|
- 'package.json'
|
||||||
|
- 'package-lock.json'
|
||||||
|
- 'packages/highlight/**'
|
||||||
|
- 'packages/server/**'
|
||||||
|
- 'packages/relay/**'
|
||||||
|
- 'packages/cli/**'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
continue-on-error: true
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: cachix/install-nix-action@v31
|
||||||
|
with:
|
||||||
|
nix_path: nixpkgs=channel:nixos-unstable
|
||||||
|
|
||||||
|
- name: Build Nix package
|
||||||
|
run: nix build .#default -o result
|
||||||
|
|
||||||
|
- name: Verify lockfile is complete
|
||||||
|
# npm silently omits resolved/integrity fields in workspace monorepos.
|
||||||
|
# Nix needs them for offline builds. See https://github.com/npm/cli/issues/4460
|
||||||
|
run: |
|
||||||
|
node scripts/fix-lockfile.mjs package-lock.json
|
||||||
|
git diff --exit-code package-lock.json || {
|
||||||
|
echo "ERROR: package-lock.json has missing resolved/integrity fields."
|
||||||
|
echo "This is a known npm bug: https://github.com/npm/cli/issues/4460"
|
||||||
|
echo "Run 'node scripts/fix-lockfile.mjs' and commit the result."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
- name: Check npmDepsHash is up to date
|
||||||
|
run: ./scripts/update-nix.sh --check
|
||||||
Generated
+27
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1772963539,
|
||||||
|
"narHash": "sha256-9jVDGZnvCckTGdYT53d/EfznygLskyLQXYwJLKMPsZs=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "9dcb002ca1690658be4a04645215baea8b95f31d",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
{
|
||||||
|
description = "Paseo - self-hosted daemon for AI coding agents";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs =
|
||||||
|
{
|
||||||
|
self,
|
||||||
|
nixpkgs,
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
supportedSystems = [
|
||||||
|
"x86_64-linux"
|
||||||
|
"aarch64-linux"
|
||||||
|
"x86_64-darwin"
|
||||||
|
"aarch64-darwin"
|
||||||
|
];
|
||||||
|
forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
|
||||||
|
pkgsFor = system: import nixpkgs { inherit system; };
|
||||||
|
in
|
||||||
|
{
|
||||||
|
packages = forAllSystems (
|
||||||
|
system:
|
||||||
|
let
|
||||||
|
pkgs = pkgsFor system;
|
||||||
|
paseo = pkgs.callPackage ./nix/package.nix { };
|
||||||
|
in
|
||||||
|
{
|
||||||
|
default = paseo;
|
||||||
|
paseo = paseo;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
nixosModules.default = self.nixosModules.paseo;
|
||||||
|
nixosModules.paseo =
|
||||||
|
{ pkgs, lib, ... }:
|
||||||
|
{
|
||||||
|
imports = [ ./nix/module.nix ];
|
||||||
|
services.paseo.package = lib.mkDefault self.packages.${pkgs.stdenv.hostPlatform.system}.default;
|
||||||
|
};
|
||||||
|
|
||||||
|
devShells = forAllSystems (
|
||||||
|
system:
|
||||||
|
let
|
||||||
|
pkgs = pkgsFor system;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
default = pkgs.mkShell {
|
||||||
|
packages = [
|
||||||
|
pkgs.nodejs_22
|
||||||
|
pkgs.python3
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
+172
@@ -0,0 +1,172 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.services.paseo;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.services.paseo = {
|
||||||
|
enable = lib.mkEnableOption "Paseo, a self-hosted daemon for AI coding agents";
|
||||||
|
|
||||||
|
package = lib.mkPackageOption pkgs "paseo" { };
|
||||||
|
|
||||||
|
user = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "paseo";
|
||||||
|
description = "User account under which Paseo runs.";
|
||||||
|
};
|
||||||
|
|
||||||
|
group = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "paseo";
|
||||||
|
description = "Group under which Paseo runs.";
|
||||||
|
};
|
||||||
|
|
||||||
|
dataDir = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default =
|
||||||
|
if cfg.user == "paseo"
|
||||||
|
then "/var/lib/paseo"
|
||||||
|
else "/home/${cfg.user}/.paseo";
|
||||||
|
defaultText = lib.literalExpression ''
|
||||||
|
if cfg.user == "paseo"
|
||||||
|
then "/var/lib/paseo"
|
||||||
|
else "/home/''${cfg.user}/.paseo"
|
||||||
|
'';
|
||||||
|
description = "Directory for Paseo state (PASEO_HOME). Stores agent data, config, and logs.";
|
||||||
|
};
|
||||||
|
|
||||||
|
port = lib.mkOption {
|
||||||
|
type = lib.types.port;
|
||||||
|
default = 6767;
|
||||||
|
description = "Port for the Paseo daemon to listen on.";
|
||||||
|
};
|
||||||
|
|
||||||
|
listenAddress = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "127.0.0.1";
|
||||||
|
description = "Address for the Paseo daemon to bind to.";
|
||||||
|
};
|
||||||
|
|
||||||
|
openFirewall = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = false;
|
||||||
|
description = "Whether to open the firewall for the Paseo daemon port.";
|
||||||
|
};
|
||||||
|
|
||||||
|
allowedHosts = lib.mkOption {
|
||||||
|
type = lib.types.either (lib.types.enum [ true ]) (lib.types.listOf lib.types.str);
|
||||||
|
default = [ ];
|
||||||
|
example = [ ".example.com" "myhost.local" ];
|
||||||
|
description = ''
|
||||||
|
Hosts allowed to connect to the Paseo daemon (DNS rebinding protection).
|
||||||
|
Localhost and IP addresses are always allowed by default.
|
||||||
|
|
||||||
|
Use a leading dot to match a domain and all its subdomains
|
||||||
|
(e.g. `".example.com"` matches `example.com` and `foo.example.com`).
|
||||||
|
|
||||||
|
Set to `true` to allow any host (not recommended).
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
relay = {
|
||||||
|
enable = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = true;
|
||||||
|
description = "Whether to enable the relay connection for remote access via app.paseo.sh.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
inheritUserEnvironment = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = cfg.user != "paseo";
|
||||||
|
defaultText = lib.literalExpression ''cfg.user != "paseo"'';
|
||||||
|
description = ''
|
||||||
|
Whether to include the user's profile PATH in the service environment.
|
||||||
|
|
||||||
|
When Paseo runs as a real user (not the default system user), AI agents
|
||||||
|
need access to the user's tools (git, ssh, etc.). This adds the user's
|
||||||
|
NixOS profile and system paths so agents can use them without manually
|
||||||
|
setting PATH.
|
||||||
|
|
||||||
|
Enabled by default when `user` is set to a non-default value.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
environment = lib.mkOption {
|
||||||
|
type = lib.types.attrsOf lib.types.str;
|
||||||
|
default = { };
|
||||||
|
example = lib.literalExpression ''
|
||||||
|
{
|
||||||
|
PASEO_RELAY_ENDPOINT = "relay.paseo.sh:443";
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
description = "Extra environment variables for the Paseo daemon.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf cfg.enable {
|
||||||
|
users.users.${cfg.user} = lib.mkIf (cfg.user == "paseo") {
|
||||||
|
isSystemUser = true;
|
||||||
|
group = cfg.group;
|
||||||
|
home = cfg.dataDir;
|
||||||
|
};
|
||||||
|
|
||||||
|
users.groups.${cfg.group} = lib.mkIf (cfg.group == "paseo") { };
|
||||||
|
|
||||||
|
systemd.tmpfiles.rules = [
|
||||||
|
"d ${cfg.dataDir} 0700 ${cfg.user} ${cfg.group} - -"
|
||||||
|
];
|
||||||
|
|
||||||
|
systemd.services.paseo = {
|
||||||
|
description = "Paseo - self-hosted daemon for AI coding agents";
|
||||||
|
after = [ "network.target" ];
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
|
||||||
|
environment = {
|
||||||
|
NODE_ENV = "production";
|
||||||
|
PASEO_HOME = cfg.dataDir;
|
||||||
|
PASEO_LISTEN = "${cfg.listenAddress}:${toString cfg.port}";
|
||||||
|
} // lib.optionalAttrs cfg.inheritUserEnvironment {
|
||||||
|
# mkForce overrides the default PATH from NixOS's systemd module (which
|
||||||
|
# only includes store paths for coreutils/grep/sed/systemd). Our PATH
|
||||||
|
# includes /run/current-system/sw/bin which is a superset of those.
|
||||||
|
PATH = lib.mkForce (lib.concatStringsSep ":" [
|
||||||
|
"/etc/profiles/per-user/${cfg.user}/bin"
|
||||||
|
"/run/current-system/sw/bin"
|
||||||
|
"/run/wrappers/bin"
|
||||||
|
"/nix/var/nix/profiles/default/bin"
|
||||||
|
]);
|
||||||
|
} // lib.optionalAttrs (cfg.allowedHosts == true) {
|
||||||
|
PASEO_ALLOWED_HOSTS = "true";
|
||||||
|
} // lib.optionalAttrs (lib.isList cfg.allowedHosts && cfg.allowedHosts != [ ]) {
|
||||||
|
PASEO_ALLOWED_HOSTS = lib.concatStringsSep "," cfg.allowedHosts;
|
||||||
|
} // cfg.environment;
|
||||||
|
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "simple";
|
||||||
|
User = cfg.user;
|
||||||
|
Group = cfg.group;
|
||||||
|
|
||||||
|
ExecStart =
|
||||||
|
"${cfg.package}/bin/paseo-server"
|
||||||
|
+ lib.optionalString (!cfg.relay.enable) " --no-relay";
|
||||||
|
|
||||||
|
Restart = "on-failure";
|
||||||
|
RestartSec = 5;
|
||||||
|
|
||||||
|
# Graceful shutdown (server handles SIGTERM with a 10s timeout)
|
||||||
|
KillSignal = "SIGTERM";
|
||||||
|
TimeoutStopSec = 15;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
environment.systemPackages = [ cfg.package ];
|
||||||
|
|
||||||
|
networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall [ cfg.port ];
|
||||||
|
};
|
||||||
|
}
|
||||||
+144
@@ -0,0 +1,144 @@
|
|||||||
|
{
|
||||||
|
lib,
|
||||||
|
stdenv,
|
||||||
|
buildNpmPackage,
|
||||||
|
nodejs_22,
|
||||||
|
python3,
|
||||||
|
makeWrapper,
|
||||||
|
# node-pty needs libuv headers on Linux
|
||||||
|
libuv,
|
||||||
|
}:
|
||||||
|
|
||||||
|
buildNpmPackage rec {
|
||||||
|
pname = "paseo";
|
||||||
|
version = (builtins.fromJSON (builtins.readFile ../package.json)).version;
|
||||||
|
|
||||||
|
src = lib.cleanSourceWith {
|
||||||
|
src = ./..;
|
||||||
|
filter = path: type:
|
||||||
|
let
|
||||||
|
baseName = builtins.baseNameOf path;
|
||||||
|
relPath = lib.removePrefix (toString ./..) path;
|
||||||
|
in
|
||||||
|
# Exclude non-daemon workspace contents (keep package.json for workspace resolution)
|
||||||
|
!(lib.hasPrefix "/packages/app/src" relPath)
|
||||||
|
&& !(lib.hasPrefix "/packages/app/assets" relPath)
|
||||||
|
&& !(lib.hasPrefix "/packages/app/android" relPath)
|
||||||
|
&& !(lib.hasPrefix "/packages/app/ios" relPath)
|
||||||
|
&& !(lib.hasPrefix "/packages/website/src" relPath)
|
||||||
|
&& !(lib.hasPrefix "/packages/website/public" relPath)
|
||||||
|
&& !(lib.hasPrefix "/packages/desktop/src" relPath)
|
||||||
|
&& !(lib.hasPrefix "/packages/desktop/src-tauri" relPath)
|
||||||
|
# Exclude test fixtures and debug files
|
||||||
|
&& !(lib.hasSuffix ".test.ts" baseName)
|
||||||
|
&& !(lib.hasSuffix ".e2e.test.ts" baseName)
|
||||||
|
&& baseName != "node_modules"
|
||||||
|
&& baseName != ".git"
|
||||||
|
&& baseName != ".paseo"
|
||||||
|
&& baseName != ".DS_Store";
|
||||||
|
};
|
||||||
|
|
||||||
|
nodejs = nodejs_22;
|
||||||
|
|
||||||
|
# To update: run `nix build` with lib.fakeHash, copy the `got:` hash.
|
||||||
|
# CI auto-updates this when package-lock.json changes (see .github/workflows/).
|
||||||
|
npmDepsHash = "sha256-gOwvUBvem1SxDMypaexz6RaHRm2xFmUT9iwOW2ErEAM=";
|
||||||
|
|
||||||
|
# Prevent onnxruntime-node's install script from running during automatic
|
||||||
|
# npm rebuild (it tries to download from api.nuget.org, which fails in the sandbox).
|
||||||
|
# We manually rebuild only node-pty in buildPhase.
|
||||||
|
npmRebuildFlags = [ "--ignore-scripts" ];
|
||||||
|
|
||||||
|
nativeBuildInputs = [
|
||||||
|
python3 # for node-gyp (node-pty compilation)
|
||||||
|
makeWrapper
|
||||||
|
];
|
||||||
|
|
||||||
|
buildInputs = lib.optionals stdenv.hostPlatform.isLinux [
|
||||||
|
libuv
|
||||||
|
];
|
||||||
|
|
||||||
|
# Don't use the default npm build hook — we need a custom build sequence
|
||||||
|
dontNpmBuild = true;
|
||||||
|
|
||||||
|
buildPhase = ''
|
||||||
|
runHook preBuild
|
||||||
|
|
||||||
|
# Rebuild only node-pty (native addon for terminal emulation).
|
||||||
|
# Speech-related native modules (sherpa-onnx, onnxruntime-node) are
|
||||||
|
# intentionally left unbuilt — they're lazily loaded and gracefully
|
||||||
|
# degrade when unavailable.
|
||||||
|
npm rebuild node-pty
|
||||||
|
|
||||||
|
# Build all daemon packages in dependency order (defined in package.json)
|
||||||
|
npm run build:daemon
|
||||||
|
|
||||||
|
runHook postBuild
|
||||||
|
'';
|
||||||
|
|
||||||
|
installPhase = ''
|
||||||
|
runHook preInstall
|
||||||
|
|
||||||
|
mkdir -p $out/lib/paseo
|
||||||
|
|
||||||
|
# Copy root package metadata
|
||||||
|
cp package.json $out/lib/paseo/
|
||||||
|
|
||||||
|
# Copy node_modules (preserving workspace symlinks)
|
||||||
|
cp -a node_modules $out/lib/paseo/
|
||||||
|
|
||||||
|
# Auto-detect which @getpaseo/* packages were built by build:daemon
|
||||||
|
# (they'll have a dist/ directory). Copy those and remove the rest.
|
||||||
|
for link in $out/lib/paseo/node_modules/@getpaseo/*; do
|
||||||
|
name=$(basename "$link")
|
||||||
|
if [ -d "packages/$name/dist" ]; then
|
||||||
|
mkdir -p "$out/lib/paseo/packages/$name"
|
||||||
|
cp "packages/$name/package.json" "$out/lib/paseo/packages/$name/"
|
||||||
|
cp -a "packages/$name/dist" "$out/lib/paseo/packages/$name/"
|
||||||
|
if [ -d "packages/$name/node_modules" ]; then
|
||||||
|
cp -a "packages/$name/node_modules" "$out/lib/paseo/packages/$name/"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
rm -f "$link"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Copy CLI bin entry
|
||||||
|
mkdir -p $out/lib/paseo/packages/cli/bin
|
||||||
|
cp packages/cli/bin/paseo $out/lib/paseo/packages/cli/bin/
|
||||||
|
|
||||||
|
# Copy extra server files referenced at runtime
|
||||||
|
for f in agent-prompt.md .env.example; do
|
||||||
|
if [ -f packages/server/$f ]; then
|
||||||
|
cp packages/server/$f $out/lib/paseo/packages/server/
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Copy server scripts (daemon-runner, supervisor) needed by CLI
|
||||||
|
if [ -d packages/server/dist/scripts ]; then
|
||||||
|
mkdir -p $out/lib/paseo/packages/server/dist/scripts
|
||||||
|
cp -a packages/server/dist/scripts/* $out/lib/paseo/packages/server/dist/scripts/
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create wrapper for the server entry point (for systemd / direct use)
|
||||||
|
mkdir -p $out/bin
|
||||||
|
makeWrapper ${nodejs}/bin/node $out/bin/paseo-server \
|
||||||
|
--add-flags "$out/lib/paseo/packages/server/dist/server/server/index.js" \
|
||||||
|
--set NODE_ENV production
|
||||||
|
|
||||||
|
# Create wrapper for the CLI
|
||||||
|
makeWrapper ${nodejs}/bin/node $out/bin/paseo \
|
||||||
|
--add-flags "$out/lib/paseo/packages/cli/dist/index.js" \
|
||||||
|
--set NODE_PATH "$out/lib/paseo/node_modules"
|
||||||
|
|
||||||
|
runHook postInstall
|
||||||
|
'';
|
||||||
|
|
||||||
|
meta = {
|
||||||
|
description = "Self-hosted daemon for Claude Code, Codex, and OpenCode";
|
||||||
|
homepage = "https://github.com/getpaseo/paseo";
|
||||||
|
license = lib.licenses.agpl3Plus;
|
||||||
|
mainProgram = "paseo";
|
||||||
|
platforms = lib.platforms.linux ++ lib.platforms.darwin;
|
||||||
|
};
|
||||||
|
}
|
||||||
Generated
+75
-25
@@ -34942,7 +34942,9 @@
|
|||||||
"expo": "*",
|
"expo": "*",
|
||||||
"react": "*",
|
"react": "*",
|
||||||
"react-native": "*"
|
"react-native": "*"
|
||||||
}
|
},
|
||||||
|
"resolved": "https://registry.npmjs.org/expo-clipboard/-/expo-clipboard-8.0.7.tgz",
|
||||||
|
"integrity": "sha512-zvlfFV+wB2QQrQnHWlo0EKHAkdi2tycLtE+EXFUWTPZYkgu1XcH+aiKfd4ul7Z0SDF+1IuwoiW9AA9eO35aj3Q=="
|
||||||
},
|
},
|
||||||
"packages/app/node_modules/react-native-nitro-modules": {
|
"packages/app/node_modules/react-native-nitro-modules": {
|
||||||
"version": "0.33.8",
|
"version": "0.33.8",
|
||||||
@@ -34959,7 +34961,9 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/colinhacks"
|
"url": "https://github.com/sponsors/colinhacks"
|
||||||
}
|
},
|
||||||
|
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
|
||||||
|
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="
|
||||||
},
|
},
|
||||||
"packages/cli": {
|
"packages/cli": {
|
||||||
"name": "@getpaseo/cli",
|
"name": "@getpaseo/cli",
|
||||||
@@ -34993,14 +34997,18 @@
|
|||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||||
}
|
},
|
||||||
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz",
|
||||||
|
"integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="
|
||||||
},
|
},
|
||||||
"packages/cli/node_modules/commander": {
|
"packages/cli/node_modules/commander": {
|
||||||
"version": "12.1.0",
|
"version": "12.1.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
},
|
||||||
|
"resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
|
||||||
|
"integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="
|
||||||
},
|
},
|
||||||
"packages/desktop": {
|
"packages/desktop": {
|
||||||
"name": "@getpaseo/desktop",
|
"name": "@getpaseo/desktop",
|
||||||
@@ -35351,7 +35359,9 @@
|
|||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
},
|
||||||
|
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.20.1.tgz",
|
||||||
|
"integrity": "sha512-j/P+yuxXfgxb+mW7OEoRCM3G47zCTDqUPivJo/VzpjbG8I9csTXtOprCf5FfOfHK4whOJny0aHuBEON+kS7CCA=="
|
||||||
},
|
},
|
||||||
"packages/server/node_modules/@modelcontextprotocol/sdk/node_modules/ajv": {
|
"packages/server/node_modules/@modelcontextprotocol/sdk/node_modules/ajv": {
|
||||||
"version": "6.12.6",
|
"version": "6.12.6",
|
||||||
@@ -35365,7 +35375,9 @@
|
|||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/epoberezkin"
|
"url": "https://github.com/sponsors/epoberezkin"
|
||||||
}
|
},
|
||||||
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||||
|
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="
|
||||||
},
|
},
|
||||||
"packages/server/node_modules/@modelcontextprotocol/sdk/node_modules/express": {
|
"packages/server/node_modules/@modelcontextprotocol/sdk/node_modules/express": {
|
||||||
"version": "5.1.0",
|
"version": "5.1.0",
|
||||||
@@ -35405,7 +35417,9 @@
|
|||||||
"funding": {
|
"funding": {
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
"url": "https://opencollective.com/express"
|
"url": "https://opencollective.com/express"
|
||||||
}
|
},
|
||||||
|
"resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz",
|
||||||
|
"integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA=="
|
||||||
},
|
},
|
||||||
"packages/server/node_modules/@modelcontextprotocol/sdk/node_modules/json-schema-traverse": {
|
"packages/server/node_modules/@modelcontextprotocol/sdk/node_modules/json-schema-traverse": {
|
||||||
"version": "0.4.1",
|
"version": "0.4.1",
|
||||||
@@ -35422,7 +35436,9 @@
|
|||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
},
|
||||||
|
"resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="
|
||||||
},
|
},
|
||||||
"packages/server/node_modules/ajv": {
|
"packages/server/node_modules/ajv": {
|
||||||
"version": "8.17.1",
|
"version": "8.17.1",
|
||||||
@@ -35436,7 +35452,9 @@
|
|||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/epoberezkin"
|
"url": "https://github.com/sponsors/epoberezkin"
|
||||||
}
|
},
|
||||||
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
|
||||||
|
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="
|
||||||
},
|
},
|
||||||
"packages/server/node_modules/ansi-regex": {
|
"packages/server/node_modules/ansi-regex": {
|
||||||
"version": "6.2.2",
|
"version": "6.2.2",
|
||||||
@@ -35446,7 +35464,9 @@
|
|||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
|
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
|
||||||
}
|
},
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
|
||||||
|
"integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="
|
||||||
},
|
},
|
||||||
"packages/server/node_modules/body-parser": {
|
"packages/server/node_modules/body-parser": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
@@ -35464,7 +35484,9 @@
|
|||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
},
|
||||||
|
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg=="
|
||||||
},
|
},
|
||||||
"packages/server/node_modules/content-disposition": {
|
"packages/server/node_modules/content-disposition": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
@@ -35474,14 +35496,18 @@
|
|||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
},
|
||||||
|
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg=="
|
||||||
},
|
},
|
||||||
"packages/server/node_modules/cookie-signature": {
|
"packages/server/node_modules/cookie-signature": {
|
||||||
"version": "1.2.2",
|
"version": "1.2.2",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.6.0"
|
"node": ">=6.6.0"
|
||||||
}
|
},
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
|
||||||
|
"integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="
|
||||||
},
|
},
|
||||||
"packages/server/node_modules/finalhandler": {
|
"packages/server/node_modules/finalhandler": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
@@ -35496,21 +35522,27 @@
|
|||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
},
|
||||||
|
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q=="
|
||||||
},
|
},
|
||||||
"packages/server/node_modules/fresh": {
|
"packages/server/node_modules/fresh": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
},
|
||||||
|
"resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="
|
||||||
},
|
},
|
||||||
"packages/server/node_modules/media-typer": {
|
"packages/server/node_modules/media-typer": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
},
|
||||||
|
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="
|
||||||
},
|
},
|
||||||
"packages/server/node_modules/merge-descriptors": {
|
"packages/server/node_modules/merge-descriptors": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
@@ -35520,7 +35552,9 @@
|
|||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
},
|
||||||
|
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="
|
||||||
},
|
},
|
||||||
"packages/server/node_modules/mime-types": {
|
"packages/server/node_modules/mime-types": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
@@ -35530,14 +35564,18 @@
|
|||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
},
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA=="
|
||||||
},
|
},
|
||||||
"packages/server/node_modules/negotiator": {
|
"packages/server/node_modules/negotiator": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
},
|
||||||
|
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="
|
||||||
},
|
},
|
||||||
"packages/server/node_modules/raw-body": {
|
"packages/server/node_modules/raw-body": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
@@ -35588,7 +35626,9 @@
|
|||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 18"
|
"node": ">= 18"
|
||||||
}
|
},
|
||||||
|
"resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw=="
|
||||||
},
|
},
|
||||||
"packages/server/node_modules/serve-static": {
|
"packages/server/node_modules/serve-static": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
@@ -35601,7 +35641,9 @@
|
|||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 18"
|
"node": ">= 18"
|
||||||
}
|
},
|
||||||
|
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ=="
|
||||||
},
|
},
|
||||||
"packages/server/node_modules/strip-ansi": {
|
"packages/server/node_modules/strip-ansi": {
|
||||||
"version": "7.1.2",
|
"version": "7.1.2",
|
||||||
@@ -35614,7 +35656,9 @@
|
|||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
|
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
|
||||||
}
|
},
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz",
|
||||||
|
"integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="
|
||||||
},
|
},
|
||||||
"packages/server/node_modules/type-is": {
|
"packages/server/node_modules/type-is": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
@@ -35626,14 +35670,18 @@
|
|||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
},
|
||||||
|
"resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="
|
||||||
},
|
},
|
||||||
"packages/server/node_modules/zod": {
|
"packages/server/node_modules/zod": {
|
||||||
"version": "3.25.76",
|
"version": "3.25.76",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/colinhacks"
|
"url": "https://github.com/sponsors/colinhacks"
|
||||||
}
|
},
|
||||||
|
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
|
||||||
|
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="
|
||||||
},
|
},
|
||||||
"packages/website": {
|
"packages/website": {
|
||||||
"name": "@getpaseo/website",
|
"name": "@getpaseo/website",
|
||||||
@@ -35667,7 +35715,9 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~6.21.0"
|
"undici-types": "~6.21.0"
|
||||||
}
|
},
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.6.tgz",
|
||||||
|
"integrity": "sha512-qm+G8HuG6hOHQigsi7VGuLjUVu6TtBo/F05zvX04Mw2uCg9Dv0Qxy3Qw7j41SidlTcl5D/5yg0SEZqOB+EqZnQ=="
|
||||||
},
|
},
|
||||||
"packages/website/node_modules/react": {
|
"packages/website/node_modules/react": {
|
||||||
"version": "19.2.4",
|
"version": "19.2.4",
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
// Workaround for https://github.com/npm/cli/issues/4460
|
||||||
|
//
|
||||||
|
// npm silently omits `resolved` and `integrity` fields from some
|
||||||
|
// package-lock.json entries in workspace monorepos (especially for
|
||||||
|
// workspace-hoisted packages). npm acknowledged this as a bug in 2022
|
||||||
|
// but has never shipped a fix.
|
||||||
|
//
|
||||||
|
// This is harmless for regular `npm ci`, but breaks offline installers
|
||||||
|
// like Nix that need every entry to have a resolved URL + integrity hash
|
||||||
|
// so they can pre-fetch all tarballs in a sandbox with no network access.
|
||||||
|
//
|
||||||
|
// This script finds incomplete entries and fills them in using `npm view`.
|
||||||
|
// It's idempotent — running it on an already-complete lockfile is a no-op.
|
||||||
|
//
|
||||||
|
// See also: https://github.com/npm/cli/issues/4263
|
||||||
|
// https://github.com/npm/cli/issues/6301
|
||||||
|
//
|
||||||
|
// Usage:
|
||||||
|
// node scripts/fix-lockfile.mjs
|
||||||
|
// node scripts/fix-lockfile.mjs path/to/package-lock.json
|
||||||
|
|
||||||
|
import fs from "fs";
|
||||||
|
import { execSync } from "child_process";
|
||||||
|
|
||||||
|
const lockPath = process.argv[2] || "package-lock.json";
|
||||||
|
const lock = JSON.parse(fs.readFileSync(lockPath, "utf8"));
|
||||||
|
|
||||||
|
// Collect workspace package roots (local packages, not from npm)
|
||||||
|
const workspaceRoots = new Set();
|
||||||
|
for (const [key, val] of Object.entries(lock.packages || {})) {
|
||||||
|
if (val.link) {
|
||||||
|
workspaceRoots.add(val.resolved || key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let fixed = 0;
|
||||||
|
|
||||||
|
for (const [key, val] of Object.entries(lock.packages || {})) {
|
||||||
|
if (
|
||||||
|
!key || // root package
|
||||||
|
key.startsWith("node_modules/") || // top-level (already has resolved)
|
||||||
|
val.link || // workspace link entry
|
||||||
|
(val.resolved && val.integrity) || // already complete
|
||||||
|
!val.version || // no version to look up
|
||||||
|
workspaceRoots.has(key) // workspace package root (local, not on npm)
|
||||||
|
)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const pkgName = key.replace(/.*node_modules\//, "");
|
||||||
|
const version = val.version;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const info = JSON.parse(
|
||||||
|
execSync(`npm view ${pkgName}@${version} --json dist`, {
|
||||||
|
encoding: "utf8",
|
||||||
|
stdio: ["pipe", "pipe", "pipe"],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
if (info.tarball && info.integrity) {
|
||||||
|
val.resolved = info.tarball;
|
||||||
|
val.integrity = info.integrity;
|
||||||
|
fixed++;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
console.error(`Warning: could not fetch info for ${pkgName}@${version}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.writeFileSync(lockPath, JSON.stringify(lock, null, 2) + "\n");
|
||||||
|
|
||||||
|
if (fixed > 0) {
|
||||||
|
console.log(`Fixed ${fixed} lockfile entries with missing resolved/integrity`);
|
||||||
|
} else {
|
||||||
|
console.log("Lockfile is already complete");
|
||||||
|
}
|
||||||
Executable
+63
@@ -0,0 +1,63 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Fix workspace-local lockfile entries and update the Nix dependency hash.
|
||||||
|
# Requires: node, npm, nix
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./scripts/update-nix.sh # fix lockfile + update hash
|
||||||
|
# ./scripts/update-nix.sh --check # verify everything is up to date (CI mode)
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||||
|
LOCK_FILE="$ROOT_DIR/package-lock.json"
|
||||||
|
PACKAGE_NIX="$ROOT_DIR/nix/package.nix"
|
||||||
|
|
||||||
|
CHECK_MODE=false
|
||||||
|
if [[ "${1:-}" == "--check" ]]; then
|
||||||
|
CHECK_MODE=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 1. Fix lockfile (add resolved/integrity for workspace-local entries)
|
||||||
|
# Workaround for https://github.com/npm/cli/issues/4460
|
||||||
|
echo "Fixing lockfile..."
|
||||||
|
node "$SCRIPT_DIR/fix-lockfile.mjs" "$LOCK_FILE"
|
||||||
|
|
||||||
|
# 2. Prefetch deps and compute hash
|
||||||
|
echo "Prefetching npm dependencies..."
|
||||||
|
|
||||||
|
# Resolve prefetch-npm-deps from the same nixpkgs pinned in flake.lock
|
||||||
|
NIXPKGS_URL="$(node -p "
|
||||||
|
const l = JSON.parse(require('fs').readFileSync('$ROOT_DIR/flake.lock', 'utf8'));
|
||||||
|
const n = l.nodes.nixpkgs.locked;
|
||||||
|
'github:' + n.owner + '/' + n.repo + '/' + n.rev;
|
||||||
|
")"
|
||||||
|
|
||||||
|
STDERR_LOG="$(mktemp)"
|
||||||
|
trap "rm -f '$STDERR_LOG'" EXIT
|
||||||
|
|
||||||
|
if ! NEW_HASH="$(nix shell "${NIXPKGS_URL}#prefetch-npm-deps" -c prefetch-npm-deps "$LOCK_FILE" 2>"$STDERR_LOG")"; then
|
||||||
|
echo "ERROR: prefetch-npm-deps failed:" >&2
|
||||||
|
tail -20 "$STDERR_LOG" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "Computed hash: $NEW_HASH"
|
||||||
|
|
||||||
|
# 3. Read current hash
|
||||||
|
CURRENT_HASH="$(grep 'npmDepsHash' "$PACKAGE_NIX" | sed 's/.*"\(.*\)".*/\1/')"
|
||||||
|
|
||||||
|
if [[ "$NEW_HASH" == "$CURRENT_HASH" ]]; then
|
||||||
|
echo "Hash is already up to date."
|
||||||
|
else
|
||||||
|
if $CHECK_MODE; then
|
||||||
|
echo "ERROR: npmDepsHash is stale."
|
||||||
|
echo " current: $CURRENT_HASH"
|
||||||
|
echo " correct: $NEW_HASH"
|
||||||
|
echo "Run ./scripts/update-nix.sh to fix."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Updating npmDepsHash in nix/package.nix..."
|
||||||
|
sed -i.bak "s|npmDepsHash = \".*\"|npmDepsHash = \"$NEW_HASH\"|" "$PACKAGE_NIX"
|
||||||
|
rm -f "$PACKAGE_NIX.bak"
|
||||||
|
echo "Updated: $CURRENT_HASH -> $NEW_HASH"
|
||||||
|
fi
|
||||||
Reference in New Issue
Block a user