npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@synopackageland/cli

v0.17.4

Published

Syno Package Land Developer Kit CLI

Readme

Syno Package Land CLI

synopkgland builds Synology DSM packages (SPK). Pick a template, declare what your app needs, and the CLI checks, builds and installs the package.

Full documentation: https://www.varvis.me/docs/ (繁體中文).

Install

npm install -g @synopackageland/cli --registry=https://registry.npmjs.org

Native Go apps need Go 1.25+ and npm. The CLI is tested on Node.js 22.19.

Quick start

synopkgland init -t native-go --with files,jobs,login --package MyApp my-app
cd my-app
(cd frontend && npm install)
synopkgland check --json
synopkgland build --arch x86_64 --json
synopkgland inspect dist/MyApp-0.1.0-0001-x86_64.spk --json
synopkgland deploy --host my-nas --arch x86_64 --json
synopkgland status --host my-nas --json

deploy --dry-run prints the deployment plan without connecting to a NAS. You can also install the SPK with Package Center's Manual Install.

Templates

| Template | Use it for | Manifest | | --- | --- | --- | | native-go | A Go backend with a Vue UI (or --ui plain) | dsm-package.yaml | | native-python, native-node, native-rust | The native-go App with a Python, Node.js (TypeScript) or Rust backend | dsm-package.yaml | | binary | A static Linux program you build yourself | dsm-package.yaml | | compose | Unchanged upstream Compose, generated override and SDK planner | dsm-package.yaml |

Existing native products with their own payload layout use backend: {kind: native} in dsm-package.yaml.

A backend in Python, Node or Rust uses backend: {runtime: python|node|rust, ...}: the CGI is the SDK's Go runtime, which checks the DSM session and permissions.json and starts the App from target/app as the package account for each request (see skills/reference/create.md).

SDK and templates

The CLI packages, deploys and checks. The app-side code is the SDK (synopackageland/syno-pkg-land-sdk): the Go module github.com/synopackageland/syno-pkg-land-sdk and the npm package @synopackageland/sdk (Vue components at @synopackageland/sdk/vue). The require github.com/synopackageland/syno-pkg-land-sdk line in backend/go.mod is the app's only SDK version; build compiles the executor of that version and checks it against the digests shipped in this CLI. A CLI builds the SDK releases of its own minor version (CLI X.Y.z builds SDK X.Y.*) that its digest table records; EXECUTOR_DIGEST_MISMATCH names the ones it knows.

The SDK repositories are private. The CLI adds github.com/synopackageland/* to its Go children's GOPRIVATE, GONOPROXY and GONOSUMDB, preserving existing environment and go env -w settings. Give git a credential that can read https://github.com/synopackageland/syno-pkg-land-sdk (check with git ls-remote); a failed fetch reports SDK_DOWNLOAD_FAILED.

CLI-only patch releases can differ from the SDK patch. Compose import and SDK defaults read the SDK pin from the installed templates; explicit App SDK declarations take precedence.

init and add read their templates from @synopackageland/templates, an exact-pinned dependency. For development against local checkouts, set SYNOPKGLAND_TEMPLATES_DIR to a templates checkout and SYNOPKGLAND_SDK_DIR to an SDK checkout; SPKs built with SYNOPKGLAND_SDK_DIR warn EXECUTOR_DIGEST_UNVERIFIED and must not be published. A Vue project packs the local SDK into its frontend (npm install --install-links) without building it: run npm ci && npm run build in $SYNOPKGLAND_SDK_DIR/ts first, and again after SDK changes.

To check the CLI's DSM terminology rules against templates without a new templates release:

SYNOPKGLAND_TEMPLATES_DIR=/path/to/templates npm run test:glossary

This compares OFF_GLOSSARY and the Italian sentence rule, including regex sources and flags. It supports declaration-only rules with read-only use in glossaryProblems; post-declaration mutations, aliases and other unsupported rule uses fail the check instead of comparing a partial snapshot. The command also fails on drift or missing source. It needs a checkout because templates 0.15.1 does not publish scripts/test/i18n.test.mjs. The npm gate runs this check when SYNOPKGLAND_TEMPLATES_DIR is set; otherwise it explicitly reports that the drift check was skipped.

Capabilities

Add them with init --with … or later with synopkgland add <capability…>.

| Capability | What the app can do | Who approves | | --- | --- | --- | | files | Browse, read and write NAS files as the signed-in DSM user | Nobody; File Station applies that user's permissions | | jobs | Run background jobs and schedules as the package | Nobody | | login | Sign users in with their DSM account, without the DSM desktop | Nobody; requires HTTPS for password sign-in | | delegate | Move files into a user's chosen folder while the user is offline | An administrator, once | | root-ops | Run fixed root/*.sh scripts for administrators | An administrator, once | | portal | Open the app from its own Application Portal alias, with a login page style (login_style changes the colour, wallpaper and title) | An administrator applies the alias; DSM 7.2-64570+ needs no executor (INFO asks DSM to reload nginx on uninstall). DSM 7.1-42661+ needs an executor to register the entry |

Delegate is implemented; its default operation is files.moveFromStaging. Device lifecycle acceptance on real hardware is still pending for delegate and root operations.

Lifecycle scripts

Every template has a dsm-scripts/ folder with DSM's own script names: preinst, postinst, preupgrade, postupgrade, preuninst, postuninst and start-stop-status. Each does nothing until you edit it. build packs them and calls each one from the generated script of the same name, after the SDK's steps (before them for preupgrade, preuninst and stop); they run as the package account, with DSM's arguments, environment and exit codes. Projects without dsm-scripts/ build as before. See synopkgland skill show lifecycle.

Root work after an install or upgrade, such as removing what an older version left, is not a lifecycle script: name a root/ program as root.start in dsm-package.yaml. The executor runs it as root once at the first start of each new version, while the administrator's approval is live, for at most 60 seconds; a failure is logged and does not block the start, and the executor status reports the result as start_step. While it runs, the executor answers only status (state: "starting") and refuses other requests with the retryable EXECUTOR_STARTING; the DSM window's approval waits for it, while a status read answers executor_starting at once and the SDK's panels check again on their own. The program cannot declare run or job, and declaring it changes the policy digest, so an administrator approves the App again.

Commands

| Command | Purpose | | --- | --- | | init | Create a project from a template | | add | Add capabilities or portal to a project | | check | Validate the manifest, capability combinations and Compose file | | build | Build the SPK for one or more architectures | | inspect | Check any SPK against the DSM 7 package format without installing it; SPKs built by synopkgland also get the build contract | | deploy | Build and install on a NAS | | status | Show package state, Open URL and capability readiness | | logs | Show package status diagnostics | | dev, test, images | Run, test and pin images for Compose projects | | skill | Read the bundled guidance for AI assistants |

--json prints one JSON object on stdout: {ok: true, command, data} on success and {ok: false, code, message, hint} on failure. See the CLI reference and error codes.

E2E helpers

@synopackageland/cli/e2e holds the DSM helpers for a package's real-NAS suite on e2e and @e2e-dev/web (optional peers, with ai and @ai-sdk/openai). The same file, src/e2e/dsm.ts, can be copied verbatim to tests/e2e/support/dsm.ts.

// e2e.config.ts
import {dsmTarget, magpieAgent} from '@synopackageland/cli/e2e';
export default {targets: [dsmTarget()], output: '.e2e/output', agents: {default: magpieAgent()}};

// tests/e2e/dsm.setup.e2e.ts: signs in once (a private saved profile when it still works)
import {dsmLoginSetup} from '@synopackageland/cli/e2e';
dsmLoginSetup({session: 'dsm', user: 'admin'});

// a test that declares {sessions: ['dsm']}
import {dismissNotifications, openApp} from '@synopackageland/cli/e2e';
const window = await openApp(app, 'SYNO.SDS.MyApp.Instance'); // closes DSM's notification cards first
await dismissNotifications(); // when a card shows later
"scripts": {"e2e:nas": "node --env-file-if-exists=.env node_modules/@synopackageland/cli/dist/e2e/dsm.js tunnel -- e2e run"}

runWithTunnel (the tunnel -- command) opens an SSH tunnel to the NAS (DSM_E2E_SSH, required, e.g. [email protected]) on a free loopback port and runs the command with DSM_E2E_URL set. It masks DSM's session cookies (id, did, _SSID), X-SYNO-TOKEN, SynoToken and _sid as *** in the command's output and in the files under DSM_E2E_OUTPUT_DIR (default .e2e), and makes that directory private (700, files 600); saved sessions keep their cookies. Set DSM_E2E_SSH to your NAS SSH destination in the environment or a local untracked .env; an unset or blank value fails before SSH or the command starts. nas.example is a fixture hostname; replace it with your own host. Accounts come from mode 600 files named by DSM_E2E_ACCOUNT_<USER>. Saved profiles use DSM_E2E_TARGET (default dsm) and the user as their names; set the target to an existing profile's target name to keep reusing it. signIn and openDesktop also register those values as e2e secrets in the test's worker, so e2e masks them in its own report. A test that forwards requests with context.route and route.fetch should end with await context.unrouteAll({behavior: 'ignoreErrors'}): a request still in flight otherwise rejects into a run error that carries its headers.

AI assistants

Every project includes AGENTS.md and a CLAUDE.md pointer. Start with synopkgland skill list and synopkgland skill show orchestrator. For UI work, read synopkgland skill show ui-vue (Vue components) or synopkgland skill show ui-json (JSON specs). From an installed frontend, npx --no syno-pkg-land-ui catalog --json lists the UI components and npx --no syno-pkg-land-ui describe Button --json describes one.

Licensing

The CLI is MIT licensed, Copyright (c) 2026 synopkgland. Third-party components keep their own terms.

  • Generated projects keep the template terms in LICENSES/TEMPLATE.txt. init does not choose a license for your app: add your own LICENSE.
  • An SPK carries its legal notices under LICENSES/ in the installed payload: TEMPLATE.txt, an optional APP.txt, SDK.txt (the SDK's MIT license, when backend/go.mod requires the SDK), Go's license, patents and third-party notices, and the Vue UI notices.
  • Vue builds generate the UI notices from the installed dependencies; run npm install in frontend/ first. A missing notice or version mismatch fails the build.
  • Keep backend/THIRD_PARTY_NOTICES.txt current when you change Go modules. Your own binaries, added modules, container images and upstream apps need their own notices.

Delegated JSON WebAPI profiles need SDK 0.11.0 or later. Declare:

capabilities:
  webapi:
    calendar:
      purpose: Create calendar reminders after workflow approval
      apis:
        SYNO.Cal.Event:
          list: [6]
          create: [6]
          delete: [6]

Use matching backend/frontend SDK pins. The profile participates in the normal executor approval digest; a profile change needs administrator approval. Users grant the named profile separately. The permanent denylist, current-user permission checks, bounded JSON and uncertain-outcome rules are in the SDK's executor/README.md. Binary downloads require later SDK support.

Raw WebAPI profiles

SDK 0.12.1 add opt-in raw responses. In capabilities.webapi.<profile>, set raw_max_bytes: 26214400 for a 25 MiB MIME limit and explicitly allow SYNO.MailClient.Message.get_original v10. Omitting the limit preserves JSON-only profiles. Changing it changes the approved policy and requires fresh user consent. Call webapi.call with response_type: "raw"; Go Apps use JobContext.WebAPI().CallRaw. Small bodies use base64; large bodies use the package's private executor-staging folder with size/SHA-256 and immediate cleanup. Do not use Attachment.download or follow X-Accel-Redirect. See the SDK raw response contract for the complete transport and CGI validation rules.

App launch mode

Native Apps declare ui.launch: dsm-window | new-page in dsm-package.yaml. Use dsm-window for frontends built with @synopackageland/ui; use new-page for every other frontend. Plain/binary scaffolds explicitly choose new-page; Vue uses the native default. An omitted native ui.launch resolves to dsm-window; the CLI does not guess a frontend's framework from its build command. new-page registers DSM type: "standalone", so the main menu and Package Center open a full page on the DSM origin. ctx.fullPage, ctx.appIcon, session/SynoToken, launch parameters and entry permissions use the same wrapper in both modes. The wrapper sets the standalone page title, favicon and device-width viewport. It retains ExtJS; pureEnv is not appropriate, and alternate Portal ports are not needed (allowAltPort is omitted).

A full page does not need Application Portal. Omit portal unless an alias is explicitly wanted; only that opt-in adds defaultAlias, login styling, Portal panels and capabilities. No Portal is a healthy default for inspect/info/doctor.

Compose uses schema-2 dsm-package.yaml and ui.launch; the default is new-page. Classless gateways use type url via the shared contract/app-launch resolver; AppWindow wrappers use standalone. Headless projects have no launcher.

Rebuilt container Apps include informational Registry check diagnostics in preinst, postinst, postupgrade and prestart. Messages use the existing nine-language catalog and end with the native recovery step: fix the cause, then use Package Center Repair. Repair re-runs the install worker and downloads missing images; it is not an SDK Retry action. Checks use only immutable image references from the packaged lock, never the Docker socket or registry credentials. They do not block cached/offline startup or establish daemon disk space, image existence or pull success. Native wrapper codes alone never identify a cause. SDK and CLI changes must ship together and Apps must be rebuilt to receive these hooks and catalogs.

See Compose for multiple upstream files, generated override/worker artifacts, image proofs and the support matrix. Containers use a fixed package identity and do not inherit a signed-in user's File Station ACL. Shared Folder intent requires admin confirmation after installation; official protected ACL/project control and bridge readback remain blocked. No SSO Server or wrapper container is added. Linux tests do not prove DSM readiness; inspect lists explicit acceptance blockers.

Control installed Apps

Use synopkgland ctl host add nas --url https://nas.example:5001 --username alice to register and authenticate a NAS in OS secure storage. ctl host list --json checks sessions; ctl list --host nas --json discovers all approved task-level commands and their schemas. Run the listed alias/command with typed flags, --help and --dry-run; writes require --yes. There is no mutable current context. Use --host a,b or --all-hosts for bounded parallel operations.

ctl preserves these process exit codes, including with --json:

| Exit code | Meaning and examples | | --- | --- | | 0 | Command completed successfully (including help and --dry-run). | | 1 | Unexpected internal error (CTL_INTERNAL), or an uncaught CLI exception. | | 2 | Authentication/session failure: CTL_NOT_AUTHENTICATED, CTL_AUTH_FAILED, CTL_CREDENTIAL_MISMATCH, or unavailable OS credential storage. | | 3 | Invalid parameters, schema validation or unknown names: CTL_PARAMS_INVALID, CTL_ARGUMENT_INVALID, CTL_APP_UNKNOWN, CTL_COMMAND_UNKNOWN. | | 4 | Permission refusal that fails the command: CTL_PERMISSION_DENIED, CTL_INVENTORY_DENIED. | | 5 | App/contract failure that fails the command: CTL_APP_ERROR, CTL_RESPONSE_INVALID, CTL_JOB_FAILED. | | 6 | Network/HTTP failure or uncertain completion: CTL_NETWORK, CTL_HTTP_ERROR, CTL_JOB_TIMEOUT, CTL_LOGOUT_FAILED. | | 7 | Confirmation required (CTL_CONFIRM_REQUIRED): a write/destructive command or host removal without confirmation; no business operation runs. | | 8 | A multi-host command has at least one failed host (including when all hosts fail); inspect each host's error. |

For example, synopkgland ctl studio drafts create --host nas --json without --yes returns ok:false with CTL_CONFIRM_REQUIRED and exits 7 once discovery and parameter validation succeed. Missing sessions fail earlier with exit 2. Discovery catches per-App description errors and excludes those Apps from its results. For example, a changed pinned description (CTL_DESCRIPTION_CHANGED) or an invalid descriptor (CTL_DESCRIPTOR_INVALID) appears in ctl list --host nas --refresh --json under data.diagnostics; the listing still returns ok:true and exits 0, even if no Apps remain. These diagnostic codes do not become process exit codes 4 or 5. Attempting to execute an excluded App with --refresh instead returns ok:false, CTL_APP_UNKNOWN and exit 3 because the App is absent from discovery results; no business operation runs.

ctl host list/show can also succeed while reporting authenticated:false and exit 0. Check discovery apps/diagnostics and session fields as well as the exit code when deciding whether an App is ready. After a network or job timeout, query the App's state before retrying a write.

Read synopkgland skill show ctl for the AI workflow, batch discovery approval, cache refresh, host removal/revocation and explicit ctl.commands authoring. API descriptions are private SPK files named synopkgland-api.json, served only by the authenticated fixed synopkgland.describe CGI operation.

Generated SDK windows expose ctx.batch([{method, params}, ...]) when the selected SDK supplies its batch factory. One to eight independent JSON reads share a request; results keep input order and retain per-method errors. A batch uses the same token recovery, request deadline and cancellation as ctx.call, and is never replayed automatically. Older SDKs and binary backends retain the single-call client. Rebuild the window with the current CLI/SDK to adopt it.