@hanzo/runtime 0.0.0-dev
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +190 -0
- package/README.md +147 -0
- package/hanzo-runtime-0.0.0-dev.tgz +0 -0
- package/hooks/typedoc-custom.mjs +640 -0
- package/jest.config.js +15 -0
- package/package.json +24 -0
- package/project.json +57 -0
- package/src/ComputerUse.ts +618 -0
- package/src/Daytona.ts +644 -0
- package/src/FileSystem.ts +414 -0
- package/src/Git.ts +303 -0
- package/src/Image.ts +643 -0
- package/src/LspServer.ts +245 -0
- package/src/ObjectStorage.ts +232 -0
- package/src/Process.ts +357 -0
- package/src/Sandbox.ts +478 -0
- package/src/Snapshot.ts +260 -0
- package/src/Volume.ts +110 -0
- package/src/code-toolbox/SandboxPythonCodeToolbox.ts +366 -0
- package/src/code-toolbox/SandboxTsCodeToolbox.ts +17 -0
- package/src/errors/DaytonaError.ts +15 -0
- package/src/index.ts +50 -0
- package/src/types/Charts.ts +193 -0
- package/src/types/ExecuteResponse.ts +33 -0
- package/src/utils/ArtifactParser.ts +58 -0
- package/src/utils/Path.ts +25 -0
- package/src/utils/Stream.ts +89 -0
- package/tsconfig.json +16 -0
- package/tsconfig.lib.json +16 -0
- package/tsconfig.spec.json +12 -0
- package/typedoc.json +33 -0
package/src/Snapshot.ts
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 Daytona Platforms Inc.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { ObjectStorageApi, SnapshotDto, SnapshotsApi, SnapshotState, CreateSnapshot } from '@daytonaio/api-client'
|
|
7
|
+
import { DaytonaError } from './errors/DaytonaError'
|
|
8
|
+
import { ObjectStorage } from './ObjectStorage'
|
|
9
|
+
import { Image } from './Image'
|
|
10
|
+
import { Resources } from './Daytona'
|
|
11
|
+
import { processStreamingResponse } from './utils/Stream'
|
|
12
|
+
|
|
13
|
+
const SNAPSHOTS_FETCH_LIMIT = 200
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Represents a Daytona Snapshot which is a pre-configured sandbox.
|
|
17
|
+
*
|
|
18
|
+
* @property {string} id - Unique identifier for the Snapshot.
|
|
19
|
+
* @property {string} organizationId - Organization ID that owns the Snapshot.
|
|
20
|
+
* @property {boolean} general - Whether the Snapshot is general.
|
|
21
|
+
* @property {string} name - Name of the Snapshot.
|
|
22
|
+
* @property {string} imageName - Name of the Image of the Snapshot.
|
|
23
|
+
* @property {boolean} enabled - Whether the Snapshot is enabled.
|
|
24
|
+
* @property {SnapshotState} state - Current state of the Snapshot.
|
|
25
|
+
* @property {number} size - Size of the Snapshot.
|
|
26
|
+
* @property {string[]} entrypoint - Entrypoint of the Snapshot.
|
|
27
|
+
* @property {number} cpu - CPU of the Snapshot.
|
|
28
|
+
* @property {number} gpu - GPU of the Snapshot.
|
|
29
|
+
* @property {number} mem - Memory of the Snapshot in GiB.
|
|
30
|
+
* @property {number} disk - Disk of the Snapshot in GiB.
|
|
31
|
+
* @property {string} errorReason - Error reason of the Snapshot.
|
|
32
|
+
* @property {Date} createdAt - Timestamp when the Snapshot was created.
|
|
33
|
+
* @property {Date} updatedAt - Timestamp when the Snapshot was last updated.
|
|
34
|
+
* @property {Date} lastUsedAt - Timestamp when the Snapshot was last used.
|
|
35
|
+
*/
|
|
36
|
+
export type Snapshot = SnapshotDto & { __brand: 'Snapshot' }
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Parameters for creating a new snapshot.
|
|
40
|
+
*
|
|
41
|
+
* @property {string} name - Name of the snapshot.
|
|
42
|
+
* @property {string | Image} image - Image of the snapshot. If a string is provided, it should be available on some registry.
|
|
43
|
+
* If an Image instance is provided, it will be used to create a new image in Daytona.
|
|
44
|
+
* @property {Resources} resources - Resources of the snapshot.
|
|
45
|
+
* @property {string[]} entrypoint - Entrypoint of the snapshot.
|
|
46
|
+
*/
|
|
47
|
+
export type CreateSnapshotParams = {
|
|
48
|
+
name: string
|
|
49
|
+
image: string | Image
|
|
50
|
+
resources?: Resources
|
|
51
|
+
entrypoint?: string[]
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Service for managing Daytona Snapshots. Can be used to list, get, create and delete Snapshots.
|
|
56
|
+
*
|
|
57
|
+
* @class
|
|
58
|
+
*/
|
|
59
|
+
export class SnapshotService {
|
|
60
|
+
constructor(
|
|
61
|
+
private snapshotsApi: SnapshotsApi,
|
|
62
|
+
private objectStorageApi: ObjectStorageApi,
|
|
63
|
+
) {}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* List all Snapshots.
|
|
67
|
+
*
|
|
68
|
+
* @returns {Promise<Snapshot[]>} List of all Snapshots accessible to the user
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* const daytona = new Daytona();
|
|
72
|
+
* const snapshots = await daytona.snapshot.list();
|
|
73
|
+
* console.log(`Found ${snapshots.length} snapshots`);
|
|
74
|
+
* snapshots.forEach(snapshot => console.log(`${snapshot.name} (${snapshot.imageName})`));
|
|
75
|
+
*/
|
|
76
|
+
async list(): Promise<Snapshot[]> {
|
|
77
|
+
let response = await this.snapshotsApi.getAllSnapshots(undefined, SNAPSHOTS_FETCH_LIMIT)
|
|
78
|
+
if (response.data.total > SNAPSHOTS_FETCH_LIMIT) {
|
|
79
|
+
response = await this.snapshotsApi.getAllSnapshots(undefined, response.data.total)
|
|
80
|
+
}
|
|
81
|
+
return response.data.items as Snapshot[]
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Gets a Snapshot by its name.
|
|
86
|
+
*
|
|
87
|
+
* @param {string} name - Name of the Snapshot to retrieve
|
|
88
|
+
* @returns {Promise<Snapshot>} The requested Snapshot
|
|
89
|
+
* @throws {Error} If the Snapshot does not exist or cannot be accessed
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* const daytona = new Daytona();
|
|
93
|
+
* const snapshot = await daytona.snapshot.get("snapshot-name");
|
|
94
|
+
* console.log(`Snapshot ${snapshot.name} is in state ${snapshot.state}`);
|
|
95
|
+
*/
|
|
96
|
+
async get(name: string): Promise<Snapshot> {
|
|
97
|
+
const response = await this.snapshotsApi.getSnapshot(name)
|
|
98
|
+
return response.data as Snapshot
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Deletes a Snapshot.
|
|
103
|
+
*
|
|
104
|
+
* @param {Snapshot} snapshot - Snapshot to delete
|
|
105
|
+
* @returns {Promise<void>}
|
|
106
|
+
* @throws {Error} If the Snapshot does not exist or cannot be deleted
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* const daytona = new Daytona();
|
|
110
|
+
* const snapshot = await daytona.snapshot.get("snapshot-name");
|
|
111
|
+
* await daytona.snapshot.delete(snapshot);
|
|
112
|
+
* console.log("Snapshot deleted successfully");
|
|
113
|
+
*/
|
|
114
|
+
async delete(snapshot: Snapshot): Promise<void> {
|
|
115
|
+
await this.snapshotsApi.removeSnapshot(snapshot.id)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Creates and registers a new snapshot from the given Image definition.
|
|
120
|
+
*
|
|
121
|
+
* @param {CreateSnapshotParams} params - Parameters for snapshot creation.
|
|
122
|
+
* @param {object} options - Options for the create operation.
|
|
123
|
+
* @param {boolean} options.onLogs - This callback function handles snapshot creation logs.
|
|
124
|
+
* @param {number} options.timeout - Default is no timeout. Timeout in seconds (0 means no timeout).
|
|
125
|
+
* @returns {Promise<void>}
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* const image = Image.debianSlim('3.12').pipInstall('numpy');
|
|
129
|
+
* await daytona.snapshot.create({ name: 'my-snapshot', image: image }, { onLogs: console.log });
|
|
130
|
+
*/
|
|
131
|
+
public async create(
|
|
132
|
+
params: CreateSnapshotParams,
|
|
133
|
+
options: { onLogs?: (chunk: string) => void; timeout?: number } = {},
|
|
134
|
+
): Promise<Snapshot> {
|
|
135
|
+
const createSnapshotReq: CreateSnapshot = {
|
|
136
|
+
name: params.name,
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (typeof params.image === 'string') {
|
|
140
|
+
createSnapshotReq.imageName = params.image
|
|
141
|
+
createSnapshotReq.entrypoint = params.entrypoint
|
|
142
|
+
} else {
|
|
143
|
+
const contextHashes = await SnapshotService.processImageContext(this.objectStorageApi, params.image)
|
|
144
|
+
createSnapshotReq.buildInfo = {
|
|
145
|
+
contextHashes,
|
|
146
|
+
dockerfileContent: params.entrypoint
|
|
147
|
+
? params.image.entrypoint(params.entrypoint).dockerfile
|
|
148
|
+
: params.image.dockerfile,
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (params.resources) {
|
|
153
|
+
createSnapshotReq.cpu = params.resources.cpu
|
|
154
|
+
createSnapshotReq.gpu = params.resources.gpu
|
|
155
|
+
createSnapshotReq.memory = params.resources.memory
|
|
156
|
+
createSnapshotReq.disk = params.resources.disk
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
let createdSnapshot = (
|
|
160
|
+
await this.snapshotsApi.createSnapshot(createSnapshotReq, undefined, {
|
|
161
|
+
timeout: (options.timeout || 0) * 1000,
|
|
162
|
+
})
|
|
163
|
+
).data
|
|
164
|
+
|
|
165
|
+
if (!createdSnapshot) {
|
|
166
|
+
throw new DaytonaError("Failed to create snapshot. Didn't receive a snapshot from the server API.")
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const terminalStates: SnapshotState[] = [SnapshotState.ACTIVE, SnapshotState.ERROR, SnapshotState.BUILD_FAILED]
|
|
170
|
+
const logTerminalStates: SnapshotState[] = [
|
|
171
|
+
...terminalStates,
|
|
172
|
+
SnapshotState.PENDING_VALIDATION,
|
|
173
|
+
SnapshotState.VALIDATING,
|
|
174
|
+
]
|
|
175
|
+
const snapshotRef = { createdSnapshot: createdSnapshot }
|
|
176
|
+
let streamPromise: Promise<void> | undefined
|
|
177
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
178
|
+
const startLogStreaming = async (onChunk: (chunk: string) => void = () => {}) => {
|
|
179
|
+
if (!streamPromise) {
|
|
180
|
+
streamPromise = processStreamingResponse(
|
|
181
|
+
() => this.snapshotsApi.getSnapshotBuildLogs(createdSnapshot.id, undefined, true, { responseType: 'stream' }),
|
|
182
|
+
(chunk) => onChunk(chunk.trimEnd()),
|
|
183
|
+
async () => logTerminalStates.includes(snapshotRef.createdSnapshot.state),
|
|
184
|
+
)
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (options.onLogs) {
|
|
189
|
+
options.onLogs(`Creating snapshot ${createdSnapshot.name} (${createdSnapshot.state})`)
|
|
190
|
+
|
|
191
|
+
if (createdSnapshot.state !== SnapshotState.BUILD_PENDING) {
|
|
192
|
+
await startLogStreaming(options.onLogs)
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
let previousState = createdSnapshot.state
|
|
197
|
+
while (!terminalStates.includes(createdSnapshot.state)) {
|
|
198
|
+
if (options.onLogs && previousState !== createdSnapshot.state) {
|
|
199
|
+
if (createdSnapshot.state !== SnapshotState.BUILD_PENDING && !streamPromise) {
|
|
200
|
+
await startLogStreaming(options.onLogs)
|
|
201
|
+
}
|
|
202
|
+
options.onLogs(`Creating snapshot ${createdSnapshot.name} (${createdSnapshot.state})`)
|
|
203
|
+
previousState = createdSnapshot.state
|
|
204
|
+
}
|
|
205
|
+
await new Promise((resolve) => setTimeout(resolve, 1000))
|
|
206
|
+
createdSnapshot = await this.get(createdSnapshot.name)
|
|
207
|
+
snapshotRef.createdSnapshot = createdSnapshot
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (options.onLogs) {
|
|
211
|
+
if (streamPromise) {
|
|
212
|
+
await streamPromise
|
|
213
|
+
}
|
|
214
|
+
if (createdSnapshot.state === SnapshotState.ACTIVE) {
|
|
215
|
+
options.onLogs(`Created snapshot ${createdSnapshot.name} (${createdSnapshot.state})`)
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (createdSnapshot.state === SnapshotState.ERROR || createdSnapshot.state === SnapshotState.BUILD_FAILED) {
|
|
220
|
+
const errMsg = `Failed to create snapshot. Name: ${createdSnapshot.name} Reason: ${createdSnapshot.errorReason}`
|
|
221
|
+
throw new DaytonaError(errMsg)
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return createdSnapshot as Snapshot
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Processes the image contexts by uploading them to object storage
|
|
229
|
+
*
|
|
230
|
+
* @private
|
|
231
|
+
* @param {Image} image - The Image instance.
|
|
232
|
+
* @returns {Promise<string[]>} The list of context hashes stored in object storage.
|
|
233
|
+
*/
|
|
234
|
+
static async processImageContext(objectStorageApi: ObjectStorageApi, image: Image): Promise<string[]> {
|
|
235
|
+
if (!image.contextList || !image.contextList.length) {
|
|
236
|
+
return []
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const pushAccessCreds = (await objectStorageApi.getPushAccess()).data
|
|
240
|
+
const objectStorage = new ObjectStorage({
|
|
241
|
+
endpointUrl: pushAccessCreds.storageUrl,
|
|
242
|
+
accessKeyId: pushAccessCreds.accessKey,
|
|
243
|
+
secretAccessKey: pushAccessCreds.secret,
|
|
244
|
+
sessionToken: pushAccessCreds.sessionToken,
|
|
245
|
+
bucketName: pushAccessCreds.bucket,
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
const contextHashes = []
|
|
249
|
+
for (const context of image.contextList) {
|
|
250
|
+
const contextHash = await objectStorage.upload(
|
|
251
|
+
context.sourcePath,
|
|
252
|
+
pushAccessCreds.organizationId,
|
|
253
|
+
context.archivePath,
|
|
254
|
+
)
|
|
255
|
+
contextHashes.push(contextHash)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return contextHashes
|
|
259
|
+
}
|
|
260
|
+
}
|
package/src/Volume.ts
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 Daytona Platforms Inc.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { VolumeDto, VolumesApi } from '@daytonaio/api-client'
|
|
7
|
+
import { DaytonaNotFoundError } from './errors/DaytonaError'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Represents a Daytona Volume which is a shared storage volume for Sandboxes.
|
|
11
|
+
*
|
|
12
|
+
* @property {string} id - Unique identifier for the Volume
|
|
13
|
+
* @property {string} name - Name of the Volume
|
|
14
|
+
* @property {string} organizationId - Organization ID that owns the Volume
|
|
15
|
+
* @property {string} state - Current state of the Volume
|
|
16
|
+
* @property {string} createdAt - Date and time when the Volume was created
|
|
17
|
+
* @property {string} updatedAt - Date and time when the Volume was last updated
|
|
18
|
+
* @property {string} lastUsedAt - Date and time when the Volume was last used
|
|
19
|
+
*/
|
|
20
|
+
export type Volume = VolumeDto & { __brand: 'Volume' }
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Service for managing Daytona Volumes.
|
|
24
|
+
*
|
|
25
|
+
* This service provides methods to list, get, create, and delete Volumes.
|
|
26
|
+
*
|
|
27
|
+
* @class
|
|
28
|
+
*/
|
|
29
|
+
export class VolumeService {
|
|
30
|
+
constructor(private volumesApi: VolumesApi) {}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Lists all available Volumes.
|
|
34
|
+
*
|
|
35
|
+
* @returns {Promise<Volume[]>} List of all Volumes accessible to the user
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* const daytona = new Daytona();
|
|
39
|
+
* const volumes = await daytona.volume.list();
|
|
40
|
+
* console.log(`Found ${volumes.length} volumes`);
|
|
41
|
+
* volumes.forEach(vol => console.log(`${vol.name} (${vol.id})`));
|
|
42
|
+
*/
|
|
43
|
+
async list(): Promise<Volume[]> {
|
|
44
|
+
const response = await this.volumesApi.listVolumes()
|
|
45
|
+
return response.data as Volume[]
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Gets a Volume by its name.
|
|
50
|
+
*
|
|
51
|
+
* @param {string} name - Name of the Volume to retrieve
|
|
52
|
+
* @param {boolean} create - Whether to create the Volume if it does not exist
|
|
53
|
+
* @returns {Promise<Volume>} The requested Volume
|
|
54
|
+
* @throws {Error} If the Volume does not exist or cannot be accessed
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* const daytona = new Daytona();
|
|
58
|
+
* const volume = await daytona.volume.get("volume-name", true);
|
|
59
|
+
* console.log(`Volume ${volume.name} is in state ${volume.state}`);
|
|
60
|
+
*/
|
|
61
|
+
async get(name: string, create = false): Promise<Volume> {
|
|
62
|
+
try {
|
|
63
|
+
const response = await this.volumesApi.getVolumeByName(name)
|
|
64
|
+
return response.data as Volume
|
|
65
|
+
} catch (error) {
|
|
66
|
+
if (
|
|
67
|
+
error instanceof DaytonaNotFoundError &&
|
|
68
|
+
create &&
|
|
69
|
+
error.message.match(/Volume with name ([\w-]+) not found/)
|
|
70
|
+
) {
|
|
71
|
+
return await this.create(name)
|
|
72
|
+
}
|
|
73
|
+
throw error
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Creates a new Volume with the specified name.
|
|
79
|
+
*
|
|
80
|
+
* @param {string} name - Name for the new Volume
|
|
81
|
+
* @returns {Promise<Volume>} The newly created Volume
|
|
82
|
+
* @throws {Error} If the Volume cannot be created
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* const daytona = new Daytona();
|
|
86
|
+
* const volume = await daytona.volume.create("my-data-volume");
|
|
87
|
+
* console.log(`Created volume ${volume.name} with ID ${volume.id}`);
|
|
88
|
+
*/
|
|
89
|
+
async create(name: string): Promise<Volume> {
|
|
90
|
+
const response = await this.volumesApi.createVolume({ name })
|
|
91
|
+
return response.data as Volume
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Deletes a Volume.
|
|
96
|
+
*
|
|
97
|
+
* @param {Volume} volume - Volume to delete
|
|
98
|
+
* @returns {Promise<void>}
|
|
99
|
+
* @throws {Error} If the Volume does not exist or cannot be deleted
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* const daytona = new Daytona();
|
|
103
|
+
* const volume = await daytona.volume.get("volume-name");
|
|
104
|
+
* await daytona.volume.delete(volume);
|
|
105
|
+
* console.log("Volume deleted successfully");
|
|
106
|
+
*/
|
|
107
|
+
async delete(volume: Volume): Promise<void> {
|
|
108
|
+
await this.volumesApi.deleteVolume(volume.id)
|
|
109
|
+
}
|
|
110
|
+
}
|