dev(extensions): [+] dev-socket

This extensions brings back the dev-socket functionality, which is
really important when testing things like broadcast, alarms, events, etc
; it saves a lot of time if you can invoke a command directly to the
backend.

This is an optional extension that will not be included in production
deployments. This is for development purposes only.
This commit is contained in:
KernelDeimos
2026-02-02 23:32:12 -05:00
committed by Eric Dubé
parent de7fbced6b
commit 5433dde6d7
7 changed files with 169 additions and 7 deletions
+4
View File
@@ -87,3 +87,7 @@ See [events.md](./events.md)
## Definitions
See [definitions.md](./definitions.md)
## Bundled extensions
- [dev-console](./dev-console.md) Dev socket for running backend commands locally (opt-in via `DEVCONSOLE=1`).
@@ -0,0 +1,21 @@
# dev-console extension
The **dev-console** extension provides a **dev socket** so you can run backend commands on a local Puter instance (e.g. commands registered in [CommandService](../../../src/backend/src/services/CommandService.js)).
## Enabling
The extension is **opt-in**. Set the environment variable `DEVCONSOLE=1` when starting Puter. The `npm run dev` script already does this:
```bash
npm run dev
```
With `DEVCONSOLE=1`, the extension registers a `dev-socket` service that creates a UNIX socket and runs command lines through CommandService.
## Usage
See [Backend dev socket](../../../src/backend/doc/dev_socket.md) for how to connect (e.g. `rlwrap nc -U ./dev.sock`) and run commands like `help`, `logs:indent`, etc.
## Location
The extension lives in `extensions/dev-console/`. It only registers the dev-socket service when `DEVCONSOLE=1`; otherwise the extension loads but does nothing, so it does not affect default runs.
+5 -1
View File
@@ -1,10 +1,14 @@
# Extension System Development Guide]
# Extension System Development Guide
## Where to find documentation
### Here
Documentation for extensions is [here](src/backend/doc/extensions/README.md).
### Bundled extensions
- **dev-console** (`extensions/dev-console/`) Dev socket for running backend commands locally. Opt-in via `DEVCONSOLE=1` (e.g. `npm run dev`). See [Backend dev socket](src/backend/doc/dev_socket.md).
### Not Here
Outdated documentation for extensions is [here](../doc/contributors/extensions/README.md).
+104
View File
@@ -0,0 +1,104 @@
/*
* Copyright (C) 2024-present Puter Technologies Inc.
*
* This file is part of Puter.
*
* Puter is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import fs from 'node:fs';
import net from 'node:net';
import path from 'node:path';
const SOCKET_NAME = 'dev.sock';
const WELCOME = [
'Puter dev socket enter a command (e.g. help) and press Enter.',
'Close the connection with Ctrl+C or by typing exit.',
'',
].join('\n');
function getSocketDir () {
if ( process.env.PUTER_DEV_SOCKET_DIR ) {
return process.env.PUTER_DEV_SOCKET_DIR;
}
const volatileRuntime = path.join(process.cwd(), 'volatile', 'runtime');
if ( fs.existsSync(volatileRuntime) ) {
return volatileRuntime;
}
return process.cwd();
}
extension.on('init', async () => {
if ( process.env.DEVCONSOLE !== '1' ) {
return;
}
const commands = extension.import('service:commands');
const socketDir = getSocketDir();
const socketPath = path.join(socketDir, SOCKET_NAME);
try {
if ( fs.existsSync(socketPath) ) {
fs.unlinkSync(socketPath);
}
fs.mkdirSync(socketDir, { recursive: true });
} catch ( err ) {
console.warn('dev-socket: could not prepare socket path', socketPath, err.message);
return;
}
const server = net.createServer((socket) => {
socket.setEncoding('utf8');
socket.write(`${WELCOME }\n> `);
let buffer = '';
socket.on('data', (chunk) => {
buffer += chunk;
const lines = buffer.split(/\r?\n/);
buffer = lines.pop() ?? '';
for ( const line of lines ) {
const trimmed = line.trim();
if ( trimmed === '' ) continue;
if ( trimmed.toLowerCase() === 'exit' ) {
socket.end();
return;
}
const log = {
log: (msg) => {
socket.write(`${String(msg) }\n`);
},
error: (msg) => {
socket.write(`${String(msg) }\n`);
},
};
commands.executeRawCommand(trimmed, log).then(() => {
socket.write('> ');
}).catch((err) => {
log.error(err?.message ?? err);
socket.write('> ');
});
}
});
socket.on('end', () => {
});
socket.on('error', () => {
});
});
server.listen(socketPath, () => {
console.log('dev-socket: socket listening at', socketPath);
});
server.on('error', (err) => {
console.warn('dev-socket: socket error', err.message);
});
});
@@ -0,0 +1,8 @@
{
"name": "@heyputer/extension-dev-console",
"version": "1.0.0",
"description": "Dev socket for running backend commands locally (opt-in via DEVCONSOLE=1)",
"main": "main.js",
"type": "module",
"private": true
}
+23 -6
View File
@@ -1,15 +1,32 @@
## Backend - dev socket
The "dev socket" allows you to interact with Puter's backend by running commands.
It's a UNIX socket created in Puter's runtime directory
(typically `./volatile/runtime`, or `/var/puter` for production instances).
It's a UNIX socket that lets you run commands registered with
[CommandService](../../src/services/CommandService.js) (e.g. `help`, `logs:indent`, `params:get`, etc.).
When in the runtime directory, you can connect to the socket with your tool
of choice. For example, using `nc` as well as `rlwrap` to get readline history:
### Enabling the dev socket
The dev socket is provided by the **dev-console extension** and is **opt-in**. To enable it:
1. Set the environment variable `DEVCONSOLE=1` when starting Puter (e.g. `npm run dev` already does this).
2. The extension lives in `extensions/dev-console/` and registers a `dev-socket` service when `DEVCONSOLE=1`.
### Socket location
The socket is created in a directory chosen as follows (in order):
- `PUTER_DEV_SOCKET_DIR` if set
- `./volatile/runtime` if it exists (typical local dev)
- otherwise the process current working directory
The socket file is named `dev.sock`.
### Connecting
When in that directory, connect with your tool of choice. For example, using `nc` and `rlwrap` for readline history:
```
rlwrap nc -U ./dev.sock
```
If it is successful you will see a message with instructions. At this point
you may enter a command. Enter the `help` command to see a list of commands.
If it is successful you will see a message with instructions. Enter a command (e.g. `help`) and press Enter.
+4
View File
@@ -72,6 +72,10 @@ key-value store, and in-memory cache.
### Adding Features to Puter
- [Implementing Drivers](./pages/drivers.md)
### Bundled extensions
- **dev-console** When `DEVCONSOLE=1` is set (e.g. `npm run dev`), the dev-console extension registers a UNIX socket (`dev.sock`) so you can run backend commands (see [CommandService](../../src/services/CommandService.js)) from a terminal. See [Backend dev socket](../dev_socket.md).
## Extensions - Planned Features
Extensions are under refactor currently. This is the checklist: