| name | zeros-server-development |
| description | Guide for developing system services in ZerOS D/server directory. Use when creating background services, system services, or server modules that run in D/server/ directory and are managed by ServerExpansion. |
ZerOS System Service Development Guide
This skill provides comprehensive guidance for developing system services in ZerOS. System services are background modules located in the D/server/ directory and managed by ServerExpansion.
Overview
What are System Services?
System services are background modules that:
- Located in
D/server/ directory (virtual path)
- Actual path:
system/service/DISK/D/server/
- Named as
server-<id>.js (e.g., server-myservice.js, id is myservice)
- Automatically loaded by ServerExpansion on system startup
- Managed through Server.* API (requires SERVER_SERVICE_MANAGE permission)
- Run in main window context (no separate process)
- Use PID 10000 (SERVER_SERVICE_PID) for kernel API calls
Key Characteristics
- Location:
D/server/server-<id>.js
- Auto-loading: Automatically scanned and loaded by ServerExpansion on boot
- Lifecycle:
__init__ (first start only) → __start__ → __stop__
- No Process: Services run in main window, not as separate processes
- PID Convention: Use
ProcessManager.SERVER_SERVICE_PID (10000) for kernel API calls
Service Structure
Required Methods
Every service module must provide these five methods (all must be functions):
| Method | Description | When Called |
|---|
__init__ | Initialize (called only once on first start) | First time Server.start(id) is called |
__start__ | Start service | Every time Server.start(id) is called |
__stop__ | Stop service | When Server.stop(id) is called |
__status__ | Return current status (any value) | When Server.status(id) is called |
__info__ | Return service information (any value) | When Server.info(id) is called |
Optional Methods
| Method | Description | When Called |
|---|
__list__ | Return configurable items list | When Server.listConfig(id) is called |
__set__ | Receive and persist configuration | When Server.setConfig(id, config) is called |
Note: If __set__ is implemented, __list__ must also be implemented.
Basic Template
(function () {
'use strict';
var _pid = (typeof ProcessManager !== 'undefined' && ProcessManager.SERVER_SERVICE_PID !== undefined)
? ProcessManager.SERVER_SERVICE_PID
: 10000;
var _running = false;
var _initialized = false;
function __init__() {
if (typeof KernelLogger !== 'undefined') {
KernelLogger.info('server-myservice', 'Service initialized');
}
_initialized = true;
}
function __start__() {
if (_running) {
if (typeof KernelLogger !== 'undefined') {
KernelLogger.warn('server-myservice', 'Service already running');
}
return;
}
_running = true;
if (typeof KernelLogger !== 'undefined') {
KernelLogger.info('server-myservice', 'Service started');
}
}
function __stop__() {
if (!_running) {
if (typeof KernelLogger !== 'undefined') {
KernelLogger.warn('server-myservice', 'Service not running');
}
return;
}
_running = false;
if (typeof KernelLogger !== 'undefined') {
KernelLogger.info('server-myservice', 'Service stopped');
}
}
function __status__() {
return {
running: _running,
initialized: _initialized
};
}
function __info__() {
return {
name: 'MyService',
version: '1.0.0',
description: 'My service description'
};
}
if (typeof window !== 'undefined' && typeof window.__ZerOS_ServerExpansion_Register__ === 'function') {
window.__ZerOS_ServerExpansion_Register__({
__init__: __init__,
__start__: __start__,
__stop__: __stop__,
__status__: __status__,
__info__: __info__
});
}
})();
Registration
Registration Function
Services must register themselves using the global registration function:
if (typeof window !== 'undefined' && typeof window.__ZerOS_ServerExpansion_Register__ === 'function') {
window.__ZerOS_ServerExpansion_Register__({
__init__: __init__,
__start__: __start__,
__stop__: __stop__,
__status__: __status__,
__info__: __info__
});
}
Important:
- Registration function:
window.__ZerOS_ServerExpansion_Register__
- Must be called at script execution end
- All five methods must be functions
- If registration fails or methods are missing, service won't be recognized
Service Lifecycle
Loading Phase
- System Boot: ServerExpansion automatically scans
D/server/ directory
- Load Scripts: All
server-*.js files are loaded
- Check Compliance: Verify registration and required methods
- No Method Calls: No service methods are called during loading
First Start
- Call
__init__: Initialize service (only once)
- Call
__start__: Start service
- Service Running: Service is now active
Subsequent Starts
- Skip
__init__: Not called again
- Call
__start__: Start service again
- Service Running: Service is active
Stop
- Call
__stop__: Stop service
- Cleanup: Service should clean up resources
- Service Stopped: Service is inactive
PID Convention
Using SERVER_SERVICE_PID
Services run in main window context and use PID 10000 for kernel API calls:
var _pid = (typeof ProcessManager !== 'undefined' && ProcessManager.SERVER_SERVICE_PID !== undefined)
? ProcessManager.SERVER_SERVICE_PID
: 10000;
Why PID 10000?
- Kernel permissions allow this PID (hasPermission passes, blacklist doesn't block)
- Services run in main window, not as separate processes
- Consistent PID for all services
Calling Kernel APIs
if (typeof NotificationManager !== 'undefined') {
NotificationManager.createNotification(_pid, {
type: 'snapshot',
title: 'Service Notification',
content: 'Message from service'
});
}
if (typeof ProcessManager !== 'undefined') {
ProcessManager.callKernelAPI(_pid, 'FileSystem.read', ['D:/file.txt']);
}
Configuration Management
Implementing Configurable Services
If your service needs configuration, implement both __list__ and __set__:
var _CONFIG_KEY = 'zeros_server_myservice_config';
var _defaultConfig = {
enabled: true,
interval: 3000,
apiUrl: ''
};
var _config = _defaultConfig;
async function _loadConfig() {
if (typeof LStorage === 'undefined') return;
try {
if (!LStorage._initialized) {
await LStorage.init();
}
var saved = LStorage.getSystemStorage(_CONFIG_KEY);
if (saved && typeof saved === 'object') {
_config = Object.assign({}, _defaultConfig, saved);
}
} catch (e) {
if (typeof KernelLogger !== 'undefined') {
KernelLogger.warn('server-myservice', 'Failed to load config: ' + e.message);
}
}
}
async function _saveConfig() {
if (typeof LStorage === 'undefined') return;
try {
if (!LStorage._initialized) {
await LStorage.init();
}
LStorage.setSystemStorage(_CONFIG_KEY, _config);
} catch (e) {
if (typeof KernelLogger !== 'undefined') {
KernelLogger.warn('server-myservice', 'Failed to save config: ' + e.message);
}
}
}
function __list__() {
return [
{
key: 'enabled',
label: '启用服务',
type: 'boolean',
value: _config.enabled
},
{
key: 'interval',
label: '轮询间隔(毫秒)',
type: 'number',
value: _config.interval
},
{
key: 'apiUrl',
label: 'API 地址',
type: 'text',
value: _config.apiUrl
}
];
}
function __set__(config) {
if (!config || typeof config !== 'object') return;
if (typeof config.enabled === 'boolean') {
_config.enabled = config.enabled;
}
if (typeof config.interval === 'number' && config.interval > 0) {
_config.interval = config.interval;
}
if (typeof config.apiUrl === 'string') {
_config.apiUrl = config.apiUrl;
}
_saveConfig();
if (_running) {
__stop__();
__start__();
}
}
Configuration Types
| Type | Description | UI Rendering |
|---|
text | Text input | Input field |
number | Number input | Number input field |
boolean | Boolean toggle | Switch/checkbox |
Common Patterns
Pattern 1: Polling Service
var _timerId = null;
var POLL_INTERVAL_MS = 3000;
function __start__() {
if (_running) return;
_running = true;
_poll();
_timerId = setInterval(_poll, POLL_INTERVAL_MS);
}
function __stop__() {
if (!_running) return;
_running = false;
if (_timerId) {
clearInterval(_timerId);
_timerId = null;
}
}
function _poll() {
fetch('/api/data')
.then(res => res.json())
.then(data => {
})
.catch(err => {
if (typeof KernelLogger !== 'undefined') {
KernelLogger.error('server-myservice', 'Poll failed: ' + err.message);
}
});
}
Pattern 2: Event Listener Service
var _listeners = [];
function __start__() {
if (_running) return;
_running = true;
if (typeof EventManager !== 'undefined') {
var handler = function(e) {
};
EventManager.registerEventHandler(_pid, 'click', handler);
_listeners.push({ type: 'click', handler: handler });
}
}
function __stop__() {
if (!_running) return;
_running = false;
if (typeof EventManager !== 'undefined') {
_listeners.forEach(function(item) {
EventManager.unregisterEventHandler(_pid, item.type, item.handler);
});
_listeners = [];
}
}
Pattern 3: POOL API Service
Services can expose APIs through POOL:
var _serviceAPI = {
doSomething: function() {
return 'result';
},
getData: function() {
return { data: 'value' };
}
};
function __start__() {
if (_running) return;
_running = true;
if (typeof POOL !== 'undefined' && typeof POOL.__ADD__ === 'function') {
try {
if (!POOL.__HAS__("SERVER_POOL")) {
POOL.__INIT__("SERVER_POOL");
}
POOL.__ADD__("SERVER_POOL", "MyService", _serviceAPI);
} catch (e) {
if (typeof KernelLogger !== 'undefined') {
KernelLogger.error('server-myservice', 'Failed to register to POOL: ' + e.message);
}
}
}
}
function __stop__() {
if (!_running) return;
_running = false;
if (typeof POOL !== 'undefined' && typeof POOL.__DELETE__ === 'function') {
try {
POOL.__DELETE__("SERVER_POOL", "MyService");
} catch (e) {
}
}
}
Pattern 4: Notification Service
function _showNotification(title, content, level) {
if (typeof NotificationManager === 'undefined') return;
var duration = 0;
if (level === 1) duration = 8000;
var p = NotificationManager.createNotification(_pid, {
type: 'snapshot',
title: title,
content: content,
duration: duration
});
if (p && typeof p.catch === 'function') {
p.catch(function(e) {
if (typeof KernelLogger !== 'undefined') {
KernelLogger.warn('server-myservice', 'Notification failed: ' + (e && e.message));
}
});
}
}
Calling Kernel APIs
Direct API Calls
Services run in main window context and can directly access kernel modules:
if (typeof NotificationManager !== 'undefined') {
NotificationManager.createNotification(_pid, options);
}
if (typeof Disk !== 'undefined') {
var nodeTree = Disk.diskSeparateMap.get('D:');
}
if (typeof LStorage !== 'undefined') {
if (!LStorage._initialized) {
await LStorage.init();
}
LStorage.setSystemStorage('key', value);
var value = LStorage.getSystemStorage('key');
}
if (typeof KernelLogger !== 'undefined') {
KernelLogger.info('server-myservice', 'Message');
KernelLogger.warn('server-myservice', 'Warning');
KernelLogger.error('server-myservice', 'Error', error);
}
Using ProcessManager.callKernelAPI
For APIs that require PID validation:
if (typeof ProcessManager !== 'undefined') {
ProcessManager.callKernelAPI(_pid, 'FileSystem.read', ['D:/file.txt'])
.then(content => {
})
.catch(err => {
if (typeof KernelLogger !== 'undefined') {
KernelLogger.error('server-myservice', 'API call failed: ' + err.message);
}
});
}
Error Handling
Standard Error Handling
function _safeOperation() {
try {
var result = riskyOperation();
return result;
} catch (e) {
if (typeof KernelLogger !== 'undefined') {
KernelLogger.error('server-myservice', 'Operation failed: ' + e.message, e);
}
return null;
}
}
function _asyncOperation() {
return fetch('/api/data')
.then(res => {
if (!res.ok) {
throw new Error('HTTP ' + res.status);
}
return res.json();
})
.catch(e => {
if (typeof KernelLogger !== 'undefined') {
KernelLogger.warn('server-myservice', 'Request failed: ' + e.message);
}
return null;
});
}
Promise Error Handling
Always handle promise rejections:
var p = NotificationManager.createNotification(_pid, options);
if (p && typeof p.catch === 'function') {
p.catch(function(e) {
KernelLogger.warn('server-myservice', 'Notification failed: ' + e.message);
});
}
NotificationManager.createNotification(_pid, options);
Best Practices
1. Always Check Module Availability
if (typeof KernelLogger !== 'undefined') {
KernelLogger.info('server-myservice', 'Message');
}
if (typeof NotificationManager !== 'undefined') {
NotificationManager.createNotification(_pid, options);
}
2. Use PID Convention
var _pid = (typeof ProcessManager !== 'undefined' && ProcessManager.SERVER_SERVICE_PID !== undefined)
? ProcessManager.SERVER_SERVICE_PID
: 10000;
3. Handle Lifecycle Properly
function __start__() {
if (_running) {
return;
}
_running = true;
}
function __stop__() {
if (!_running) {
return;
}
_running = false;
}
4. Clean Up Resources
function __stop__() {
_running = false;
if (_timerId) {
clearInterval(_timerId);
_timerId = null;
}
_listeners.forEach(function(item) {
EventManager.unregisterEventHandler(_pid, item.type, item.handler);
});
_listeners = [];
if (typeof POOL !== 'undefined') {
POOL.__DELETE__("SERVER_POOL", "MyService");
}
}
5. Use KernelLogger for Logging
if (typeof KernelLogger !== 'undefined') {
KernelLogger.info('server-myservice', 'Service started');
KernelLogger.warn('server-myservice', 'Warning message');
KernelLogger.error('server-myservice', 'Error message', error);
KernelLogger.debug('server-myservice', 'Debug message');
}
6. Handle Promise Rejections
var p = asyncOperation();
if (p && typeof p.catch === 'function') {
p.catch(function(e) {
if (typeof KernelLogger !== 'undefined') {
KernelLogger.error('server-myservice', 'Operation failed: ' + e.message);
}
});
}
7. Load Configuration on Init
function __init__() {
_loadConfig();
_initialized = true;
}
8. Save Configuration Properly
function __set__(config) {
_saveConfig();
if (_running) {
__stop__();
__start__();
}
}
Complete Example: Polling Notification Service
(function () {
'use strict';
var _pid = (typeof ProcessManager !== 'undefined' && ProcessManager.SERVER_SERVICE_PID !== undefined)
? ProcessManager.SERVER_SERVICE_PID
: 10000;
var _running = false;
var _initialized = false;
var _timerId = null;
var POLL_INTERVAL_MS = 3 * 60 * 1000;
var API_URL = 'https://api.example.com/notifications';
var _lastCheckTime = null;
var _receivedIds = [];
function __init__() {
if (typeof KernelLogger !== 'undefined') {
KernelLogger.info('server-notifications', 'Service initialized');
}
_initialized = true;
}
function __start__() {
if (_running) return;
_running = true;
if (typeof KernelLogger !== 'undefined') {
KernelLogger.info('server-notifications', 'Service started');
}
_checkNotifications();
_timerId = setInterval(_checkNotifications, POLL_INTERVAL_MS);
}
function __stop__() {
if (!_running) return;
_running = false;
if (typeof KernelLogger !== 'undefined') {
KernelLogger.info('server-notifications', 'Service stopped');
}
if (_timerId) {
clearInterval(_timerId);
_timerId = null;
}
}
function __status__() {
return {
running: _running,
initialized: _initialized,
lastCheckTime: _lastCheckTime,
apiUrl: API_URL || '(not configured)'
};
}
function __info__() {
return {
name: 'NotificationService',
version: '1.0.0',
description: 'Polling notification service'
};
}
function _checkNotifications() {
if (!API_URL) {
if (typeof KernelLogger !== 'undefined') {
KernelLogger.debug('server-notifications', 'API URL not configured');
}
return;
}
_lastCheckTime = Date.now();
var req = typeof fetch !== 'undefined' ? fetch(API_URL) : null;
if (!req || typeof req.then !== 'function') {
if (typeof KernelLogger !== 'undefined') {
KernelLogger.warn('server-notifications', 'fetch not available');
}
return;
}
req.then(function(res) {
if (!res || !res.ok) {
throw new Error('HTTP ' + (res ? res.status : 'unknown'));
}
return res.json();
}).then(function(body) {
if (!body || typeof body !== 'object') return;
var notifications = Array.isArray(body.data) ? body.data : [body.data];
notifications.forEach(function(notif) {
if (!notif || !notif.id) return;
if (_receivedIds.indexOf(notif.id) >= 0) return;
_receivedIds.push(notif.id);
_showNotification(notif.title, notif.content, notif.level || 0);
});
}).catch(function(e) {
if (typeof KernelLogger !== 'undefined') {
KernelLogger.warn('server-notifications', 'Check failed: ' + (e && e.message));
}
});
}
function _showNotification(title, content, level) {
if (typeof NotificationManager === 'undefined') return;
var duration = 0;
if (level === 1) duration = 8000;
var p = NotificationManager.createNotification(_pid, {
type: 'snapshot',
title: title || 'Notification',
content: content,
duration: duration
});
if (p && typeof p.catch === 'function') {
p.catch(function(e) {
if (typeof KernelLogger !== 'undefined') {
KernelLogger.warn('server-notifications', 'Notification failed: ' + (e && e.message));
}
});
}
}
if (typeof window !== 'undefined' && typeof window.__ZerOS_ServerExpansion_Register__ === 'function') {
window.__ZerOS_ServerExpansion_Register__({
__init__: __init__,
__start__: __start__,
__stop__: __stop__,
__status__: __status__,
__info__: __info__
});
}
})();
File Location and Naming
File Structure
system/service/DISK/D/server/
├── server-notice.js # Notice service
├── server-translate.js # Translation service
├── server-processmemory.js # Process memory service
├── server-aiassistant.js # AI assistant service
└── server-myservice.js # Your new service
Naming Convention
- File name:
server-<id>.js (lowercase, hyphen-separated)
- Service ID:
<id> part of filename (e.g., myservice from server-myservice.js)
- Registration: Must call
__ZerOS_ServerExpansion_Register__
Testing
Manual Testing
- Place file in
system/service/DISK/D/server/server-myservice.js
- Restart system or call
Server.loadAll to reload services
- Check service list:
Server.listServices
- Start service:
Server.start('myservice')
- Check status:
Server.status('myservice')
- Stop service:
Server.stop('myservice')
Using Terminal
service list
service start myservice
service stop myservice
service status myservice
service reload
Common Issues and Solutions
Issue: Service not loaded
Cause: Registration failed or methods missing
Solution:
- Check registration function is called
- Verify all five methods are functions
- Check console for errors
Issue: "ServerExpansion 仅允许通过进程管理器 kernelAPI 调用"
Cause: Directly calling ServerExpansion methods
Solution: Use kernelAPI.call('Server.xxx', args) instead
Issue: Permission denied
Cause: Missing SERVER_SERVICE_MANAGE permission
Solution: Ensure program declares PermissionManager.PERMISSION.SERVER_SERVICE_MANAGE
Issue: API calls fail
Cause: Using wrong PID or module not available
Solution:
- Use
ProcessManager.SERVER_SERVICE_PID (10000)
- Check module availability before use
- Handle promise rejections
References
- Service Module Guide:
docs/SERVER/ServiceModule.md
- ServerExpansion API:
docs/API/ServerExpansion.md
- Server README:
docs/SERVER/README.md
- Kernel Interaction Skill:
dev/skill/zeros-kernel-interaction/SKILL.md
- NotificationManager API:
docs/API/NotificationManager.md
- LStorage API:
docs/API/LStorage.md