Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 50 additions & 2 deletions javascript/sdk/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { Repl } from './repl'
import { env } from './env'
import type {
ApiExecResponse,
ApiExecResponseResult,
ApiExecResultResponse,
ApiExecResultStreamResponse,
CreateMachineResponse,
ListMachinesResponse,
WhoamiResponse,
Expand Down Expand Up @@ -42,6 +43,27 @@ export class ForeverVM {
return await response.json()
}

async *#getStream(path: string) {
const response = await fetch(`${this.#baseUrl}${path}`, { headers: this.#headers })
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`)
}

if (!response.body) return
Comment thread
jakelazaroff marked this conversation as resolved.

const decoder = new TextDecoderStream()
const reader = response.body.pipeThrough(decoder).getReader()
while (true) {
const { done, value = '' } = await reader.read()
if (done) return

const lines = value.split('\n').filter((line) => line.trim())
for (const line of lines) {
yield JSON.parse(line)
Comment thread
jakelazaroff marked this conversation as resolved.
}
}
}

async #post(path: string, body?: object) {
const response = await fetch(`${this.#baseUrl}${path}`, {
method: 'POST',
Expand Down Expand Up @@ -83,10 +105,17 @@ export class ForeverVM {
})
}

async execResult(machineName: string, instructionSeq: number): Promise<ApiExecResponseResult> {
async execResult(machineName: string, instructionSeq: number): Promise<ApiExecResultResponse> {
return await this.#get(`/v1/machine/${machineName}/exec/${instructionSeq}/result`)
}

async *execResultStream(
machineName: string,
instructionSeq: number,
): AsyncGenerator<ApiExecResultStreamResponse> {
yield* this.#getStream(`/v1/machine/${machineName}/exec/${instructionSeq}/stream-result`)
}

repl(machineName?: string): Repl {
return new Repl({
machine: machineName,
Expand Down Expand Up @@ -128,4 +157,23 @@ if (import.meta.vitest) {
const result = await fvm.execResult(machine_name, instruction_seq as number)
expect(result.result.value).toBe('567')
})

test('execResultStream', async () => {
const fvm = new ForeverVM({ token: FOREVERVM_TOKEN, baseUrl: FOREVERVM_API_BASE })
const { machine_name } = await fvm.createMachine()
const { instruction_seq } = await fvm.exec('for i in range(10): print(i)\n"done"', machine_name)
expect(instruction_seq).toBe(0)

let i = 0
for await (const item of fvm.execResultStream(machine_name, instruction_seq as number)) {
if (item.type === 'result') {
expect(item.result.value).toBe("'done'")
} else if (item.type === 'output') {
expect(item.chunk.stream).toBe('stdout')
expect(item.chunk.data).toBe(`${i}`)
expect(item.chunk.seq).toBe(i)
i += 1
}
}
})
}
37 changes: 3 additions & 34 deletions javascript/sdk/src/repl.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { env } from './env'
import type { ExecResponse } from './types'
import WebSocket from 'isomorphic-ws'

import { env } from './env'
import type { ExecResponse, MessageFromServer, StandardOutput } from './types'

export interface ExecOptions {
timeoutSeconds?: number
interrupt?: boolean
Expand All @@ -18,38 +19,6 @@ export type MessageToServer = {
request_id: number
}

export type StandardOutput = {
stream: 'stdout' | 'stderr'
data: string
seq: number
}

export type MessageFromServer =
| {
type: 'connected'
machine_name: string
}
| {
type: 'exec_received'
seq: number // TODO: rename to instruction_id
request_id: number
}
| {
type: 'result'
instruction_id: number
result: ExecResponse
}
| {
type: 'output'
chunk: StandardOutput
instruction_id: number
}
| {
type: 'error'
code: string
id: string
}

interface ReplOptions {
baseUrl?: string
token?: string
Expand Down
46 changes: 45 additions & 1 deletion javascript/sdk/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,45 @@
export interface StandardOutput {
stream: 'stdout' | 'stderr'
data: string
seq: number
}

export interface ConnectedMessageFromServer {
type: 'connected'
machine_name: string
}

export interface ExecMessageFromServer {
type: 'exec_received'
seq: number // TODO: rename to instruction_id
request_id: number
}

export interface ResultMessageFromServer {
type: 'result'
instruction_id: number
result: ExecResponse
}

export interface OutputMessageFromServer {
type: 'output'
chunk: StandardOutput
instruction_id: number
}

export interface ErrorMessageFromServer {
type: 'error'
code: string
id: string
}

export type MessageFromServer =
| ConnectedMessageFromServer
| ExecMessageFromServer
| ResultMessageFromServer
| OutputMessageFromServer
| ErrorMessageFromServer

export interface WhoamiResponse {
account: string
}
Expand Down Expand Up @@ -40,10 +82,12 @@ export interface ExecResponse {
runtime_ms: number
}

export interface ApiExecResponseResult {
export interface ApiExecResultResponse {
instruction_seq: number
result: ExecResponse
value?: string | null
error?: string
runtime_ms: number
}

export type ApiExecResultStreamResponse = OutputMessageFromServer | ResultMessageFromServer