gionuibk's picture
Upload folder using huggingface_hub
61d39e2 verified

Extensions - Implementing Drivers

Puter's concept of drivers has existed long before the extension system was refined, and to keep things moving forward it has become easier to develop Puter drivers in extensions than anywhere else in Puter's source. If you want to build a driver, an extension is the recommended way to do it.

What are Puter drivers?

Puter drivers are all called through the /drivers/call endpoint, so they can be thought of as being "above" the HTTP layer. When a method on a driver throws an error you will still receive a 200 HTTP status response because the the invocation - from the HTTP layer - was successful.

A driver response follows this structure:

{
    "success": true,
    "service": {
        "name": "implementation-name"
    },
    "result": "any type of value goes here",
    "metadata": {}
}

There exists an example driver called hello-world. This driver implements a method called greet with the optional parameter subject which returns a string greeting either World (default) or the specified subject.

await puter.call('hello-world', 'no-frills', 'greet', { subject: 'Dave' });

Let's break it down:

'hello-world'

'hello-world' is the name of an "interface". An interface can be thought of a contract of what inputs are allowed and what outputs are expected. For example the hello-world interface specifies that there must be a method called greet and it should return a string representing a greeting.

To add another example, an interface called weather specify a method called forcast5day that always returns a list of 5 objects with a particular structure.

no-frills

'no-frills' is a simple - "no frills" (nothing extra) - implementation of the hello-world interface. All it does is return the string:

`Hello, ${subject ?? 'World'}!`

'greet'

greet is the method being called. It's the only method on the hello-world interface.

{ subject: 'Dave' }

These are the arguments to the greet method. The arguments specify that we want to say "Hello" to Dave. Hopefully he doesn't ask us to open the pod bay doors, or if he does we hopefully have extensions to add a driver interface and driver implementation for the pod bay doors so that we can interact with them.

Drivers in Extensions

The hellodriver extension adds the hello-world interface like this:

extension.on('create.interfaces', event => {
    // createInterface is the only method on this `event`
    event.createInterface('hello-world', {
        description: 'Provides methods for generating greetings',
        methods: {
            greet: {
                description: 'Returns a greeting',
                parameters: {
                    subject: {
                        type: 'string',
                        optional: true
                    },
                    locale: {
                        type: 'string',
                        optional: true
                    },
                }
            }
        }
    })
});

The hellodriver extension adds the no-frills implementation for hello-world like this:

extension.on('create.drivers', event => {
    event.createDriver('hello-world', 'no-frills', {
        greet ({ subject }) {
            return `Hello, ${subject ?? 'World'}!`;
        }
    });
});`

You can pass an instance of a class for a driver implementation as well:

class Greeter {
    greet ({ subject }) {
        return `Hello, ${subject ?? 'World'}!`;
    }
}

extension.on('create.drivers', event => {
    event.createDriver('hello-world', 'no-frills', new Greeter());
});`

Instances of classes being supported may seem to be implied by the example before this one, but that is not the case. What's shown here is that function members of the object passed to createDriver will not be "bound" (have their .bind() method called with a different object as the instance variable).

Permission Denied

When you try to access a driver as any user other than the default admin user, it will not work unless permission has been granted.

The hellodriver extension grants permission to all clients using the following snippet:

extension.on('create.permissions', event => {
    event.grant_to_everyone('service:no-frills:ii:hello-world');
});

The create.permissions event's event object has a few methods you can use depending on the desired granularity:

  • grant_to_everyone - grants permission to all users
  • grant_to_users - grants permission to only registered users (i.e. not to temporary/guest users)