| name | fnos-plugin |
| description | Use this skill when the user wants to create a fnOS (飞牛OS) NAS plugin, build an .fpk package, develop a fnOS app, or asks about fnOS plugin development. Scaffolds complete plugin projects with build scripts. |
| argument-hint | <plugin-name> <display-name> <port> |
| allowed-tools | ["Read","Write","Edit","Bash","Glob","Grep"] |
fnOS Plugin Developer
Create complete 飞牛OS (fnOS) native .fpk plugin projects. This skill scaffolds the full directory structure, config files, lifecycle scripts, Python backend, Vue frontend, and a one-click build script.
The user provides: $ARGUMENTS in the format <plugin-name> <display-name> <port>.
- plugin-name: lowercase with hyphens, e.g.
photo-viewer
- display-name: display title, e.g.
"Photo Viewer"
- port: Web UI port, e.g.
8590
What to Generate
Create apps/<plugin-name>/ with this structure:
apps/<plugin-name>/
├── fnos/
│ ├── manifest # App metadata
│ ├── ICON.PNG # 64x64 placeholder icon
│ ├── ICON_256.PNG # 256x256 placeholder icon
│ ├── <AppName>.sc # Firewall port rules
│ ├── bin/<plugin-name>-server # Shell launcher
│ ├── app/
│ │ ├── server.py # Python 3 stdlib HTTP backend
│ │ └── static/index.html # Vue 3 CDN single-page frontend
│ ├── cmd/service-setup # fnOS lifecycle hooks
│ ├── config/privilege # Run-as permissions
│ ├── config/resource # Port and resource config
│ ├── ui/config # Desktop launcher JSON
│ ├── ui/images/64.png # Launcher icon
│ └── wizard/config # Install wizard JSON
└── build.sh # One-click packager → .fpk
Step-by-step Instructions
1. Create all config files
Use the templates in references/config-templates.md to generate each config file. Replace all {plugin-name}, {display-name}, {port} placeholders.
2. Create cmd/service-setup
Use the template in references/service-setup-template.md. This file is critical — it MUST include the ui/ symlink fix in service_postinst().
3. Create bin/-server
A shell script that launches Python:
#!/bin/sh
APP_DIR="${TRIM_APPDEST}"
APP_DATA_DIR=$1
export HOME="$APP_DATA_DIR"
cd "$APP_DIR" || exit 1
exec python3 ./app/server.py --port "${TRIM_SERVICE_PORT:-<port>}" --data-dir "$APP_DATA_DIR"
4. Create app/server.py
Python 3 stdlib HTTP server skeleton with:
HTTPServer + custom handler
- Static file serving for
app/static/
- JSON API routing (
do_GET / do_POST)
argparse for --port and --data-dir
signal handlers for SIGTERM/SIGINT
- Zero external dependencies
5. Create app/static/index.html
Vue 3 CDN single-page app with:
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js">
- Inline CSS with card-based layout
- Composition API (
setup())
fetch() API wrapper
6. Generate placeholder icons
Use Node.js to generate solid-color PNG files (64x64 and 256x256) using zlib. If node is unavailable, create minimal valid PNGs.
7. Create build.sh
This is the most complex file. It must:
- Package
app/, bin/, ui/ into app.tgz
- Compute md5 checksum and write to manifest
- Inline the complete
shared/cmd/common lifecycle framework (~300 lines)
- Generate
cmd/main, cmd/installer, and all hook scripts
- Overlay
cmd/service-setup from the app
- Assemble everything into
<name>_<version>_<platform>.fpk (tar.gz)
Use the complete build.sh reference in references/build-script.md.
8. Report to user
After generation, tell the user:
- Replace placeholder icons with real ones
- Edit
server.py to implement business logic
- Edit
index.html to implement UI
- Run
bash build.sh to package
- Upload
.fpk to fnOS → App Center → Manual Install
- SSH debug:
/var/apps/<name>/cmd/main status|start|log
Critical Gotchas
These are real issues discovered during development — not theoretical:
-
ui/ symlink is REQUIRED: fnOS does NOT auto-extract ui/ from the fpk to /var/apps/. You must put ui/ inside app.tgz AND create a symlink in service_postinst(). Without this, the desktop icon won't appear or the app opens in a new browser tab instead of an embedded window.
-
app.tgz extraction path: fnOS extracts app.tgz to /vol*/@appcenter/<appname>/ (= $TRIM_APPDEST), NOT to /var/apps/.
-
TRIM_PKGVAR safety: The shared framework rejects TRIM_PKGVAR values that don't start with /vol.
-
manifest alignment: Key-value pairs use fixed-width alignment at column 16 with spaces (not tabs).
-
@ directories: When listing NAS volumes, filter out @-prefixed system directories (@appdata, @appcenter, etc.).