enso/docs/language-server/protocol-project-manager.md
Dmitry Bushev 0b8a0c493a
Allow project manager to read files (#11204)
close #11187

Changelog:
- add: `--filesystem-read-path` project manager command to read a file path and return its contents to stdout
2024-10-01 16:52:29 +00:00

31 KiB

layout title category tags order
developer-doc Enso Protocol Project Manager Message Specification language-server
language-server
protocol
specification
3

Enso Protocol Project Manager Message Specification

This document contains the specification of the Enso protocol messages that pertain to the project manager component. Please familiarise yourself with the common features of the protocol before reading this document.

For information on the design and architecture of the protocol, as well as its transport formats, please look here.

Types

There are a number of types that are used only within the project server's protocol messages. These are specified here.

ProjectMetadata

This type represents information about a project.

Format

interface ProjectMetadata {
  /**
   * The name of the project.
   */
  name: String;

  /**
   * The namespace of the project.
   */
  namespace: String;

  /**
   * The project id.
   */
  id: UUID;

  /**
   * Enso Engine version to use for the project, represented by a semver version
   * string.
   *
   * If the edition associated with the project could not be resolved, the
   * engine version may be missing.
   */
  engineVersion?: String;

  /**
   * The project creation time.
   */
  created: UTCDateTime;

  /**
   * The last opened datetime.
   */
  lastOpened?: UTCDateTime;
}

MissingComponentAction

This type specifies what action should be taken if a component required to complete an operation is missing.

  • Fail will make the operation fail if any components are missing.
  • Install will try to install any missing components, unless they are marked as broken.
  • ForceInstallBroken will try to install all missing components, even if some of them are marked as broken.

Format

type MissingComponentAction = Fail | Install | ForceInstallBroken;

ProgressUnit

This type specifies the unit of progress updates related to a task.

Format

type ProgressUnit = Bytes | Other;

EngineVersion

This type represents an installed or available engine version.

Format

interface EngineVersion {
  /** Semver string of engine version. */
  version: String;

  /** Specifies if that version is marked as broken. */
  markedAsBroken: bool;
}

RunningState

This type represents information about a state of the (potentially) running project.

Format

interface RunningStatus {
  /**
   * If true, the project is open and (still) accepting new connections.
   * False, if the project is currently shutdown.
   */
  open: bool;

  /**
   * If true, the project is currently running but in a soft shutdown mode.
   * False, if the project is either not running or it is not shutting down.
   */
  shuttingDown: bool;
}

FileSystemEntry

A directory entry in the fileystem operations.

Format

type FileSystemEntry = FileEntry | DirectoryEntry | ProjectEntry;

interface FileEntry {
  path: string;
  attributes: Attributes;
}

interface DirectoryEntry {
  path: string;
  attributes: Attributes;
}

interface ProjectEntry {
  path: string;
  attributes: Attributes;
  metadata: ProjectMetadata;
}

Attributes

Basic attributes of a filesystem entry.

Format

interface Attributes {
  creationTime: UTCDateTime;
  lastAccessTime: UTCDateTime;
  lastModifiedTime: UTCDateTime;
  byteSize: number;
}

UTCDateTime

Time in UTC time zone represented as ISO-8601 string.

Format

type UTCDateTime = string;

File System Management Operations

The project-manager binary provides Cli interface to do basic filesystem operations.

List Directories

List directory returning information about filesystem entries together with the project metadata if the listed directory contains a project.

Parameters

project-manager --filesystem-list {path}

Result

{
  entries: FileSystemEntry[];
}

Errors

Create Directory

Create directory with the specified path.

Parameters

project-manager --filesystem-create-directory {path}

Result

null;

Errors

Delete Directory

Deletes directory with the specified path.

Parameters

project-manager --filesystem-delete-directory {path}

Result

null;

Errors

Move File Or Directory

Moves file or directory from target path to the destination path.

Parameters

project-manager --filesystem-move-from {path} --filesystem-move-to {path}

Result

null;

Errors

Read File

Read the provided path and return the contents to stdout.

Parameters

project-manager --filesystem-read-path {path}

Result

null;

Errors

Write to File

Writes bytes from stdin to the provided path.

Parameters

echo 'Hello World!' | project-manager --filesystem-write-path {path}

Result

null;

Errors

Project Management Operations

The primary responsibility of the project managers is to allow users to manage their projects.

project/open

This message requests that the project manager open a specified project. This operation also includes spawning an instance of the language server open on the specified project.

To open a project, an engine version that is specified in project settings needs to be installed. If missingComponentAction is set to Install or ForceInstallBroken, this action will install any missing components, otherwise, an error will be reported if a component is missing. A typical usage scenario may consist of first trying to open the project without installing missing components. If that fails with the MissingComponentError, the client can ask the user if they want to install the missing components and re-attempt the action.

  • Type: Request
  • Direction: Client -> Server
  • Connection: Protocol
  • Visibility: Public

Parameters

{
  projectId: UUID;

  /**
   * Specifies how to handle missing components.
   *
   * If not provided, defaults to `Fail`.
   */
  missingComponentAction?: MissingComponentAction;

  /**
   * Custom directory with the user projects.
   */
  projectsDirectory?: string;
}

Result

{
  /**
   * The version of the started language server represented by a semver version
   * string.
   */
  engineVersion: String;

  /**
   * The endpoint used for JSON-RPC protocol.
   */
  languageServerJsonAddress: IPWithSocket;

  /**
   * The optional endpoint used for secure JSON-RPC protocol.
   */
  languageServerSecureJsonAddress?: IPWithSocket;

  /**
   * The endpoint used for binary protocol.
   */
  languageServerBinaryAddress: IPWithSocket;

  /**
   * The optional endpoint used for secure binary protocol.
   */
  languageServerSecureBinaryAddress?: IPWithSocket;

  // The name of the project as it is opened.
  projectName: String;

  // The normalized name of the project.
  projectNormalizedName: String;

  // The namespace of the project.
  projectNamespace: String;
}

Errors

  • ProjectNotFoundError to signal that the project doesn't exist.
  • ProjectDataStoreError to signal problems with underlying data store.
  • ProjectOpenError to signal failures during server boot.
  • MissingComponentError to signal that the component required to open the project was missing (only in case missingComponentAction was set to fail).
  • BrokenComponentError to signal that the component required to open the project was being installed but is marked as broken (only in case missingComponentAction was set to install).
  • ComponentInstallationError to signal that the installation of a missing component has failed.
  • ProjectManagerUpgradeRequired to signal that the requested engine version requires a more recent project manager, so an upgrade has to be performed before continuing.

project/close

This message requests that the project manager close a specified project. This operation includes shutting down the language server gracefully so that it can persist state to disk as needed.

  • Type: Request
  • Direction: Client -> Server
  • Connection: Protocol
  • Visibility: Public

Parameters

interface ProjectCloseRequest {
  projectId: UUID;
}

Result

{
}

Errors

project/list

This message requests that the project manager lists all user's projects. The list of projects is sorted by the open time.

  • Type: Request
  • Direction: Client -> Server
  • Connection: Protocol
  • Visibility: Public

Parameters

interface ProjectListRequest {
  numberOfProjects?: Int;
}

Result

interface ProjectListResponse {
  projects: [ProjectMetadata];
}

Errors

project/create

This message requests the creation of a new project.

To create a project, an engine version associated with it needs to be installed. Depending on missingComponentAction, any components required to complete the operation are missing will be installed or a failure will be reported.

  • Type: Request
  • Direction: Client -> Server
  • Connection: Protocol
  • Visibility: Public

Parameters

interface ProjectCreateRequest {
  /** Name of the project to create. */
  name: String;

  /** The name of the project template to create. */
  projectTemplate?: String;

  /**
   * Enso Engine version to use for the project.
   *
   * Possible values are:
   * - a semver version string identifying an Enso engine version,
   * - `default` to use the current default.
   *
   * The field is optional - if it is missing, it is treated as `default`.
   */
  version?: String;

  /**
   * Specifies how to handle missing components.
   *
   * If not provided, defaults to `Fail`.
   */
  missingComponentAction?: MissingComponentAction;

  /**
   * Custom directory with the user projects.
   */
  projectsDirectory?: string;
}

Result

interface ProjectCreateResponse {
  projectId: UUID;
  projectName: string;
  projectNormalizedName: string;
}

Errors

project/rename

This message requests the renaming of a project.

  • Type: Request
  • Direction: Client -> Server
  • Connection: Protocol
  • Visibility: Public

Parameters

interface ProjectRenameRequest {
  projectId: UUID;

  name: String;

  /**
   * Custom directory with the user projects.
   */
  projectsDirectory?: string;
}

Result

null

Errors

project/delete

This message requests the deletion of a project.

  • Type: Request
  • Direction: Client -> Server
  • Connection: Protocol
  • Visibility: Public

Parameters

interface ProjectDeleteRequest {
  projectId: UUID;

  /**
   * Custom directory with the user projects.
   */
  projectsDirectory?: string;
}

Result

{
}

Errors

project/listSample

This request lists the sample projects that are available to the user.

  • Type: Request
  • Direction: Client -> Server
  • Connection: Protocol
  • Visibility: Public

Parameters

interface ProjectListSampleRequest {
  numProjects: Int;
}

Result

interface ProjectListSampleResponse {
  projects: [ProjectMetadata];
}

Errors

TBC

project/status

This request asks for the current state of the project.

  • Type: Request
  • Direction: Client -> Server
  • Connection: Protocol
  • Visibility: Public

Parameters

interface ProjectStatusRequest {
  projectID: UUID;
}

Result

interface ProjectStatusResponse {
  status: RunningStatus;
}

project/duplicate

This message requests to make a copy of the project.

  • Type: Request
  • Direction: Client -> Server
  • Connection: Protocol
  • Visibility: Public

Parameters

interface ProjectDuplicateRequest {
  /**
   * The project to duplicate.
   */
  projectId: UUID;

  /**
   * Custom directory with the user projects.
   */
  projectsDirectory?: string;
}

Result

interface ProjectDuplicateResponse {
  projectId: UUID;
  projectName: string;
  projectNormalizedName: string;
}

Errors

Action Progress Reporting

Some actions, especially those related to installation of new components may take a long time (for example because big packages need to be downloaded).

The protocol includes notifications tied to such actions that can be used to display progress bars.

Each task has a lifecycle of being initialized with a task/started notification (which contains a UUID that identifies that task), being updated with task/progress-update and finalized with task/finished. task/finished may include an error (but please note that regardless of the task-related error, the error will also be reported for the original request associated with the task, for example as ComponentInstallationError returned for the project/open request that triggered the installation).

Tasks are sent while an operation is being processed and a single operation may consist of several (sub)tasks.

For example, when opening a project the flow may be following:

  • project/open request sent to the server
  • notification task/started (downloading the archive)
  • multiple notifications task/progress-update related to that task
  • notification task/finished
  • notification task/started (extracting the archive)
  • multiple notifications task/progress-update related to that task
  • notification task/finished
  • reply to the original project/open request

All task progress updates happen within the response/request flow (up to a possible reordering of messages).

task/started

Indicates that a long running task has been started.

Currently only used when components are being installed to show installation progress.

  • Type: Notification
  • Direction: Server -> Client
  • Connection: Protocol
  • Visibility: Public

Parameters

interface TaskStartNotification {
  /**
   * Unique identifier of the task, used to correlate progress updates and the
   * finished notification.
   */
  taskId: UUID;

  /**
   * Name of the operation this task is related to, for example
   * `project/open`.
   */
  relatedOperation: String;

  /** Unit in which progress of this task is measured. */
  unit: ProgressUnit;

  /**
   * Indicates total expected progress.
   *
   * May be missing, as it is not always known, for example when downloading a
   * file of unknown size or waiting on a lock.
   */
  total?: Long;
}

task/progress-update

Indicates a progress update for a specific task.

  • Type: Notification
  • Direction: Server -> Client
  • Connection: Protocol
  • Visibility: Public

Parameters

interface TaskUpdateNotification {
  taskId: UUID;

  /** Optional message explaining current status of the task. */
  message?: String;

  /** Indicates amount of progress, for example: count of processed bytes. */
  done: Long;
}

task/finished

Indicates that a task has been finished, either successfully or with an error.

  • Type: Notification
  • Direction: Server -> Client
  • Connection: Protocol
  • Visibility: Public

Parameters

interface TaskFinishedNotification {
  taskId: UUID;

  /** Optional message informing about task completion. */
  message?: String;

  /** Specifies if the task succeeded or failed. */
  success: bool;
}

Runtime Version Management

engine/list-installed

Lists engine versions currently installed.

Please note that the broken marks associated with each engine currently represent the state at the moment of installation. As of now, if the broken mark has been added later, it is not updated automatically.

  • Type: Request
  • Direction: Client -> Server
  • Connection: Protocol
  • Visibility: Public

Parameters

null;

Result

interface EngineVersionListResponse {
  /** List of installed engines. */
  versions: [EngineVersion];
}

Errors

TBC

engine/list-available

Queries the repository to list all engine versions that are available to be installed.

  • Type: Request
  • Direction: Client -> Server
  • Connection: Protocol
  • Visibility: Public

Parameters

null;

Result

interface EngineVersionListResponse {
  /** List of available engines. */
  versions: [EngineVersion];
}

Errors

engine/install

Requests to install the specified engine version. If that version is already installed, it has no effect.

  • Type: Request
  • Direction: Client -> Server
  • Connection: Protocol
  • Visibility: Public

Parameters

interface EngineInstallRequest {
  /** Semver string of engine version that should be installed. */
  version: String;

  /**
   * Specifies whether the engine should be installed even if it is marked as
   * broken.
   *
   * If not provided, defaults to `false`.
   */
  forceInstallBroken?: bool;
}

Result

null;

Errors

  • BrokenComponentError to signal that the requested engine version is marked as broken (only in case forceInstallBroken was set to false).
  • ComponentInstallationError to signal that the installation of a missing component has failed.
  • ProjectManagerUpgradeRequired to signal that the requested engine version requires a more recent project manager, so an upgrade has to be performed before continuing.

engine/uninstall

Requests to uninstall the specified engine version.

If that version was not installed, it has no effect.

  • Type: Request
  • Direction: Client -> Server
  • Connection: Protocol
  • Visibility: Public

Parameters

interface EngineUninstallRequest {
  /** Semver string of engine version that should be uninstalled. */
  version: String;
}

Result

null;

Errors

Logging Service

logging-service/get-endpoint

Requests the endpoint for connecting to the logging service.


  • Type: Request
  • Direction: Client -> Server
  • Connection: Protocol
  • Visibility: Public

Parameters

null;

Result

interface LoggingServiceEndpointResponse {
  uri: String;
}

Errors

Language Server Management

The project manager is also responsible for managing the language server. This means that it needs to be able to spawn the process, but also tell the process when to shut down.

A language server process is spawned within the project/open call. That call returns endpoints that the client can use to connect to the language server. When project/close is called, the language server is shutdown. Moreover, between these two calls, the project manager sends heartbeat messages to the language server to check if it is still running. In case that it has crashed, a restart is attempted.

Errors

The project manager component has its own set of errors. This section is not a complete specification and will be updated as new errors are added.

Besides the required code and message fields, the errors may have a data field which can store additional error-specific payload.

MissingComponentError

Signals that a component required to complete the action was missing, but the action did not ask for it to be automatically installed.

"error" : {
  "code" : 4020,
  "message" : "Engine 1.2.3 is required to complete the action but it is not installed."
}

BrokenComponentError

Signals that a component that was being installed is marked as broken, but the option to forcibly install broken components was not set.

This error may handled by warning the user about the broken version or suggesting to upgrade the project and asking to confirm using the broken version. If the user wants to ignore the warning, the operation can be reattempted with the option to forcibly install broken components.

"error" : {
  "code" : 4021,
  "message" : "Engine 1.2.3 is marked as broken."
}

ProjectManagerUpgradeRequired

Signals that installation of a missing compoment has been attempted, but the required engine version requires a newer version of project manager than what is currently running.

This error type includes the optional data field which is an object with a field minimumRequiredVersion that is a semver string of the project manager version that is required to complete the related action.

"error" : {
  "code" : 4022,
  "message" : "Project manager 1.2.3 is required to install the requested engine. Please upgrade.",
  "payload": {
    "minimumRequiredVersion": "1.2.3"
  }
}

ComponentInstallationError

Signals that installation of a missing component has been attempted but it has failed.

"error" : {
  "code" : 4023,
  "message" : "A problem occurred when trying to find the release: Cannot find release `enso-1.2.3-not-published`."
}

ComponentUninstallationError

Signals that uninstallation of a component has failed.

"error" : {
  "code" : 4024,
  "message" : "The requested engine version is not installed."
}

ComponentRepositoryUnavailable

Signals that the repository is unavailable and could not be queried (usually caused by lack of internet connection).

"error" : {
  "code" : 4025,
  "message" : "Could not connect to github.com"
}

ProjectNameValidationError

Signals validation failures.

"error" : {
  "code" : 4001,
  "message" : "Cannot create project with empty name"
}

ProjectDataStoreError

Signals problems with underlying data store.

"error" : {
  "code" : 4002,
  "message" : "Cannot load project index"
}

ProjectExistsError

Signals that the project already exists.

"error" : {
  "code" : 4003,
  "message" : "Project with the provided name exists"
}

ProjectNotFoundError

Signals that the project doesn't exist.

"error" : {
  "code" : 4004,
  "message" : "Project with the provided id does not exist"
}

ProjectOpenError

Signals that the project cannot be open due to boot failures.

"error" : {
  "code" : 4005,
  "message" : "A boot failure."
}

ProjectNotOpenError

Signals that cannot close project that is not open.

"error" : {
  "code" : 4006,
  "message" : "Cannot close project that is not open"
}

ProjectOpenByOtherPeersError

Signals that cannot close a project that is open by other clients.

"error" : {
  "code" : 4007,
  "message" : "Cannot close project because it is open by other peers"
}

CannotRemoveOpenProjectError

Signals that cannot remove open project.

"error" : {
  "code" : 4008,
  "message" : "Cannot remove open project"
}

ProjectCloseError

Signals failures during shutdown of a server.

"error" : {
  "code" : 4009,
  "message" : "A shutdown failure."
}

LanguageServerError

Signals generic language server errors.

"error" : {
  "code" : 4010,
  "message" : "The language server is unresponsive"
}

GlobalConfigurationAccessError

Signals that the global configuration file could not be accessed or parsed.

"error" : {
  "code" : 4011,
  "message" : "The global configuration file is malformed."
}

ProjectCreateError

Signals that an error occurred when creating the project.

"error" : {
  "code" : 4012,
  "message" : "Could not create the project."
}

LoggingServiceUnavailable

Signals that the logging service is not available.

"error" : {
  "code" : 4013,
  "message" : "The logging service has failed to boot."
}