| name | zeros-bin-development |
| description | Guide for developing bin programs (CLI commands) in ZerOS. Use when creating command-line tools, terminal commands, or CLI programs that run in D:/bin/ directory. |
ZerOS Bin Program Development Guide
This skill provides comprehensive guidance for developing bin programs (CLI commands) in ZerOS. Bin programs are command-line tools that run in the terminal and are located in the D:/bin/ directory.
Overview
What are Bin Programs?
Bin programs are CLI (Command-Line Interface) programs that:
- Located in
D:/bin/ directory (virtual path)
- Actual path:
system/service/DISK/D/bin/
- Executed directly from terminal by command name
- Automatically terminate after execution
- Interact with users through terminal I/O
Key Characteristics
- Type: CLI (Command-Line Interface)
- Location:
D:/bin/<command-name>.js
- Execution: Automatically executed when user types command name in terminal
- Lifecycle: Initialize → Execute → Auto-terminate
- Terminal Access: Via
initArgs.terminal or TerminalAPI
Program Structure
Basic Template
(function(window) {
'use strict';
const COMMAND_NAME = {
pid: null,
terminal: null,
_kernelAPI: null,
_closing: false,
__info__: function() {
return {
name: 'COMMAND_NAME',
type: 'CLI',
version: '1.0.0',
description: 'Command description',
author: 'Your Name',
copyright: '© 2025',
permissions: typeof PermissionManager !== 'undefined' ? [
PermissionManager.PERMISSION.EVENT_LISTENER,
] : [],
metadata: {
autoStart: false,
priority: 1,
allowMultipleInstances: true
}
};
},
__init__: async function(pid, initArgs = {}) {
this.pid = pid;
this.terminal = initArgs.terminal;
this._kernelAPI = initArgs.kernelAPI || null;
if (!this.terminal) {
throw new Error('CLI program requires terminal environment');
}
const args = initArgs.args || [];
setTimeout(async () => {
try {
if (args.includes('-h') || args.includes('--help')) {
this._showUsage();
setTimeout(() => this._selfClose(), 300);
return;
}
await this._executeCommand(args);
setTimeout(() => this._selfClose(), 300);
} catch (error) {
if (typeof KernelLogger !== 'undefined') {
KernelLogger.error('COMMAND_NAME', `Execution failed: ${error.message}`, error);
}
this.terminal.write(`Error: ${error.message}\n`);
setTimeout(() => this._selfClose(), 300);
}
}, 0);
},
_executeCommand: async function(args) {
this.terminal.write('Command executed\n');
},
_showUsage: function() {
this.terminal.write('Usage: command [options] [arguments]\n');
this.terminal.write('\n');
this.terminal.write('Options:\n');
this.terminal.write(' -h, --help Show this help message\n');
this.terminal.write('\n');
this.terminal.write('Examples:\n');
this.terminal.write(' command arg1 arg2\n');
},
_selfClose: async function() {
if (this._closing) return;
this._closing = true;
await new Promise(resolve => setTimeout(resolve, 200));
if (!this.pid) return;
try {
if (this._kernelAPI && typeof this._kernelAPI.call === 'function') {
await this._kernelAPI.call('Process.requestSelfTermination', []);
} else if (typeof ProcessManager !== 'undefined') {
await ProcessManager.callKernelAPI(this.pid, 'Process.requestSelfTermination', []);
}
} catch (error) {
if (typeof ProcessManager !== 'undefined' && ProcessManager.killProgram) {
try {
await ProcessManager.killProgram(this.pid, true);
} catch (e) {
}
}
}
},
__exit__: async function() {
this.terminal = null;
this._kernelAPI = null;
}
};
if (typeof window !== 'undefined') {
window.COMMAND_NAME = COMMAND_NAME;
}
if (typeof POOL !== 'undefined' && typeof POOL.__ADD__ === 'function') {
try {
if (!POOL.__HAS__("APPLICATION_SHARED_POOL")) {
POOL.__INIT__("APPLICATION_SHARED_POOL");
}
POOL.__ADD__("APPLICATION_SHARED_POOL", "COMMAND_NAME", COMMAND_NAME);
} catch (e) {
}
}
})(window);
Terminal Interaction
Accessing Terminal
Method 1: Via initArgs (Recommended)
__init__: async function(pid, initArgs) {
this.terminal = initArgs.terminal;
if (!this.terminal) {
throw new Error('CLI program requires terminal environment');
}
this.terminal.write('Hello from CLI\n');
}
Method 2: Via TerminalAPI
__init__: async function(pid, initArgs) {
const TerminalAPI = POOL.__GET__("APPLICATION_SHARED_POOL", "TerminalAPI");
if (TerminalAPI) {
TerminalAPI.write('Hello from CLI\n');
}
}
Terminal Output
this.terminal.write('Hello World\n');
this.terminal.write({
text: 'Error: File not found\n',
color: 'red',
bold: true
});
this.terminal.write({
text: 'Success: Operation completed\n',
color: 'green'
});
this.terminal.write('\x1B[2J\x1B[H');
TerminalAPI Methods
const TerminalAPI = POOL.__GET__("APPLICATION_SHARED_POOL", "TerminalAPI");
if (TerminalAPI) {
TerminalAPI.write('Text\n');
TerminalAPI.write({ text: 'Styled', color: 'red', bold: true });
TerminalAPI.clear();
TerminalAPI.setCwd('D:/project');
const env = TerminalAPI.getEnv();
TerminalAPI.setEnv({ KEY: 'value' });
TerminalAPI.focus();
const tabId = TerminalAPI.createTab('My Tab');
TerminalAPI.switchTab(tabId);
TerminalAPI.closeTab(tabId);
const tabs = TerminalAPI.getTabs();
}
Command-Line Arguments
Parsing Arguments
__init__: async function(pid, initArgs) {
const args = initArgs.args || [];
if (args.includes('-h') || args.includes('--help')) {
this._showUsage();
return;
}
let verbose = false;
let outputFile = null;
const positionalArgs = [];
for (let i = 0; i < args.length; i++) {
const arg = args[i];
if (arg === '-v' || arg === '--verbose') {
verbose = true;
} else if (arg === '-o' || arg === '--output') {
outputFile = args[++i];
} else if (arg.startsWith('-')) {
this.terminal.write(`Unknown option: ${arg}\n`);
this._showUsage();
return;
} else {
positionalArgs.push(arg);
}
}
await this._executeCommand(positionalArgs, { verbose, outputFile });
}
Argument Validation
_validateArgs: function(args) {
if (args.length === 0) {
this.terminal.write('Error: Missing required arguments\n');
this._showUsage();
return false;
}
if (args.length > 10) {
this.terminal.write('Error: Too many arguments\n');
return false;
}
return true;
}
Kernel API Calls
Using Bound API (Recommended)
__init__: async function(pid, initArgs) {
this._kernelAPI = initArgs.kernelAPI;
const content = await this._kernelAPI.call('FileSystem.read', ['D:/file.txt']);
await this._kernelAPI.call('Notification.create', [{
title: 'Info',
content: 'Operation completed',
type: 'info'
}]);
}
Common Kernel APIs for Bin Programs
const content = await this._kernelAPI.call('FileSystem.read', ['D:/path/to/file.txt']);
await this._kernelAPI.call('FileSystem.write', ['D:/path/to/file.txt', content]);
const files = await this._kernelAPI.call('FileSystem.list', ['D:/path/to/dir']);
const processes = ProcessManager.listProcesses();
const processInfo = ProcessManager.getProcessInfo(pid);
const sysInfo = SystemInformation.getSystemInfo();
const response = await this._kernelAPI.call('Network.request', [url, options]);
const value = await this._kernelAPI.call('Storage.read', ['key']);
await this._kernelAPI.call('Storage.write', ['key', value]);
Error Handling
Standard Error Handling Pattern
__init__: async function(pid, initArgs) {
setTimeout(async () => {
try {
await this._executeCommand(initArgs.args || []);
setTimeout(() => this._selfClose(), 300);
} catch (error) {
if (typeof KernelLogger !== 'undefined') {
KernelLogger.error('COMMAND_NAME', `Error: ${error.message}`, error);
}
this.terminal.write({
text: `Error: ${error.message}\n`,
color: 'red',
bold: true
});
if (error.message.includes('argument') || error.message.includes('option')) {
this._showUsage();
}
setTimeout(() => this._selfClose(), 300);
}
}, 0);
}
Error Types
if (error.message.includes('没有权限') || error.message.includes('permission')) {
this.terminal.write('Error: Permission denied\n');
this.terminal.write('This command requires additional permissions.\n');
}
if (error.message.includes('不存在') || error.message.includes('not found')) {
this.terminal.write('Error: File or directory not found\n');
}
if (error.message.includes('invalid') || error.message.includes('无效')) {
this.terminal.write('Error: Invalid argument\n');
this._showUsage();
}
Self-Termination
Why Auto-Terminate?
Bin programs should automatically terminate after execution because:
- They are one-shot commands (like
ls, ps, cat)
- They don't maintain interactive state
- Terminal expects them to complete and return control
Termination Methods
Method 1: Bound API (Recommended)
_selfClose: async function() {
if (this._closing) return;
this._closing = true;
await new Promise(resolve => setTimeout(resolve, 200));
if (this._kernelAPI && typeof this._kernelAPI.call === 'function') {
await this._kernelAPI.call('Process.requestSelfTermination', []);
}
}
Method 2: Standard API
_selfClose: async function() {
if (this._closing) return;
this._closing = true;
await new Promise(resolve => setTimeout(resolve, 200));
if (typeof ProcessManager !== 'undefined') {
await ProcessManager.callKernelAPI(this.pid, 'Process.requestSelfTermination', []);
}
}
Method 3: Force Termination (Fallback)
_selfClose: async function() {
if (this._closing) return;
this._closing = true;
try {
if (this._kernelAPI) {
await this._kernelAPI.call('Process.requestSelfTermination', []);
}
} catch (error) {
if (typeof ProcessManager !== 'undefined' && ProcessManager.killProgram) {
await ProcessManager.killProgram(this.pid, true);
}
}
}
Common Patterns
Pattern 1: Simple Command (No Arguments)
_executeCommand: async function() {
this.terminal.write('System Information:\n');
this.terminal.write(`Version: ${SystemInformation.getSystemVersion()}\n`);
this.terminal.write(`Kernel: ${SystemInformation.getKernelVersion()}\n`);
}
Pattern 2: Command with Options
_executeCommand: async function(args) {
const options = this._parseOptions(args);
if (options.verbose) {
this.terminal.write('Verbose mode enabled\n');
}
await this._doWork(options);
}
Pattern 3: File Processing Command
_executeCommand: async function(args) {
if (args.length === 0) {
this.terminal.write('Error: No file specified\n');
this._showUsage();
return;
}
const filePath = args[0];
try {
const content = await this._kernelAPI.call('FileSystem.read', [filePath]);
this.terminal.write(content);
} catch (error) {
this.terminal.write(`Error reading file: ${error.message}\n`);
}
}
Pattern 4: Interactive Command
_executeCommand: async function(args) {
this.terminal.write('Enter value: ');
}
Pattern 5: Command with Subcommands
_executeCommand: async function(args) {
const subcommand = args[0];
switch (subcommand) {
case 'list':
await this._cmdList(args.slice(1));
break;
case 'start':
await this._cmdStart(args.slice(1));
break;
case 'stop':
await this._cmdStop(args.slice(1));
break;
default:
this.terminal.write(`Unknown subcommand: ${subcommand}\n`);
this._showUsage();
}
}
Best Practices
1. Always Check Terminal Availability
__init__: async function(pid, initArgs) {
if (!initArgs.terminal) {
throw new Error('CLI program requires terminal environment');
}
this.terminal = initArgs.terminal;
}
2. Use setTimeout for Execution
__init__: async function(pid, initArgs) {
setTimeout(async () => {
await this._executeCommand(initArgs.args || []);
setTimeout(() => this._selfClose(), 300);
}, 0);
}
3. Always Auto-Terminate
setTimeout(() => this._selfClose(), 300);
4. Provide Help Information
_showUsage: function() {
this.terminal.write('Usage: command [options] [arguments]\n');
this.terminal.write('\n');
this.terminal.write('Options:\n');
this.terminal.write(' -h, --help Show help\n');
this.terminal.write('\n');
this.terminal.write('Examples:\n');
this.terminal.write(' command arg1\n');
}
5. Handle Errors Gracefully
try {
await this._executeCommand(args);
} catch (error) {
this.terminal.write(`Error: ${error.message}\n`);
if (error.message.includes('argument')) {
this._showUsage();
}
} finally {
setTimeout(() => this._selfClose(), 300);
}
6. Use Bound API for Kernel Calls
await this._kernelAPI.call('Process.requestSelfTermination', []);
await ProcessManager.callKernelAPI(this.pid, 'Process.requestSelfTermination', []);
7. Prevent Duplicate Termination
_closing: false,
_selfClose: async function() {
if (this._closing) return;
this._closing = true;
}
8. Clean Up Resources
__exit__: async function() {
this.terminal = null;
this._kernelAPI = null;
}
File Location and Naming
File Structure
system/service/DISK/D/bin/
├── ps.js # Process list command
├── service.js # Service management command
├── vim.js # Text editor
├── netport.js # Network port command
└── mycommand.js # Your new command
Naming Convention
- File name: Lowercase, matches command name
- Program name: Uppercase constant (e.g.,
PS, SERVICE)
- Command name: Same as file name (without
.js)
Example:
- File:
ps.js
- Program object:
PS
- Command:
ps
Permissions
Common Permissions for Bin Programs
permissions: typeof PermissionManager !== 'undefined' ? [
PermissionManager.PERMISSION.EVENT_LISTENER,
PermissionManager.PERMISSION.KERNEL_DISK_READ,
PermissionManager.PERMISSION.KERNEL_DISK_WRITE,
PermissionManager.PERMISSION.SERVER_SERVICE_MANAGE,
] : []
Complete Example: Simple List Command
(function(window) {
'use strict';
const LS = {
pid: null,
terminal: null,
_kernelAPI: null,
_closing: false,
__info__: function() {
return {
name: 'LS',
type: 'CLI',
version: '1.0.0',
description: 'List directory contents',
author: 'ZerOS Team',
copyright: '© 2025',
permissions: typeof PermissionManager !== 'undefined' ? [
PermissionManager.PERMISSION.EVENT_LISTENER,
PermissionManager.PERMISSION.KERNEL_DISK_LIST
] : [],
metadata: {
autoStart: false,
priority: 1,
allowMultipleInstances: true
}
};
},
__init__: async function(pid, initArgs = {}) {
this.pid = pid;
this.terminal = initArgs.terminal;
this._kernelAPI = initArgs.kernelAPI || null;
if (!this.terminal) {
throw new Error('LS requires terminal environment');
}
const args = initArgs.args || [];
setTimeout(async () => {
try {
if (args.includes('-h') || args.includes('--help')) {
this._showUsage();
setTimeout(() => this._selfClose(), 300);
return;
}
const path = args[0] || 'D:';
await this._listDirectory(path);
setTimeout(() => this._selfClose(), 300);
} catch (error) {
this.terminal.write(`ls: ${error.message}\n`);
setTimeout(() => this._selfClose(), 300);
}
}, 0);
},
_listDirectory: async function(path) {
try {
const files = await this._kernelAPI.call('FileSystem.list', [path]);
if (files.length === 0) {
this.terminal.write('(empty)\n');
return;
}
for (const file of files) {
const type = file.type === 'directory' ? 'd' : '-';
const name = file.name;
this.terminal.write(`${type} ${name}\n`);
}
} catch (error) {
throw new Error(`Cannot list directory '${path}': ${error.message}`);
}
},
_showUsage: function() {
this.terminal.write('Usage: ls [directory]\n');
this.terminal.write('\n');
this.terminal.write('List directory contents.\n');
this.terminal.write('\n');
this.terminal.write('Examples:\n');
this.terminal.write(' ls\n');
this.terminal.write(' ls D:/application\n');
},
_selfClose: async function() {
if (this._closing) return;
this._closing = true;
await new Promise(resolve => setTimeout(resolve, 200));
if (this._kernelAPI && typeof this._kernelAPI.call === 'function') {
await this._kernelAPI.call('Process.requestSelfTermination', []);
}
},
__exit__: async function() {
this.terminal = null;
this._kernelAPI = null;
}
};
if (typeof window !== 'undefined') {
window.LS = LS;
}
})(window);
Testing
Manual Testing
- Place file in
system/service/DISK/D/bin/mycommand.js
- Open terminal
- Type command name:
mycommand
- Verify output and behavior
- Test with different arguments
- Test error cases
Common Test Cases
- Command without arguments
- Command with valid arguments
- Command with invalid arguments
- Command with help flag (
-h, --help)
- Command with missing required arguments
- Command with permission errors
- Command with file not found errors
Common Issues and Solutions
Issue: "CLI程序需要终端环境"
Cause: Terminal not available in initArgs
Solution: Ensure program is started from terminal, not GUI
Issue: "API调用拒绝(PID校验)"
Cause: PID validation fails in VM/CLI programs
Solution: Use bound API this._kernelAPI.call() instead of ProcessManager.callKernelAPI(this.pid, ...)
Issue: Program doesn't terminate
Cause: Forgot to call _selfClose() or termination failed
Solution:
- Always call
_selfClose() after execution
- Use bound API for termination
- Add fallback force termination
Issue: Output not visible
Cause: Program terminates before output completes
Solution: Add delay before termination:
setTimeout(() => this._selfClose(), 300);
References
- TerminalAPI:
docs/API/TerminalAPI.md
- ProcessManager:
docs/API/ProcessManager.md
- Developer Guide:
docs/DEVELOPER_GUIDE.md
- Kernel Interaction Skill:
dev/skill/zeros-kernel-interaction/SKILL.md
- Terminal Commands:
docs/TERMINAL_COMMANDS.md