@mastra/daytona 0.5.2 → 0.6.0
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/CHANGELOG.md +75 -0
- package/README.md +2 -6
- package/dist/index.cjs +98 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +99 -8
- package/dist/index.js.map +1 -1
- package/dist/sandbox/index.d.ts +39 -1
- package/dist/sandbox/index.d.ts.map +1 -1
- package/package.json +12 -12
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Daytona,
|
|
1
|
+
import { Daytona, SandboxState, DaytonaNotFoundError } from '@daytonaio/sdk';
|
|
2
2
|
import { SandboxProcessManager, MastraSandbox, SandboxNotReadyError, ProcessHandle } from '@mastra/core/workspace';
|
|
3
3
|
import { createHash } from 'crypto';
|
|
4
4
|
|
|
@@ -738,6 +738,29 @@ var DaytonaSandbox = class _DaytonaSandbox extends MastraSandbox {
|
|
|
738
738
|
name = "DaytonaSandbox";
|
|
739
739
|
provider = "daytona";
|
|
740
740
|
// Non-optional (initialized by base class when mount() exists)
|
|
741
|
+
/**
|
|
742
|
+
* Networking capability: public HTTPS URLs for sandbox ports.
|
|
743
|
+
* Daytona exposes ports through preview links (`getPreviewLink(port)`) —
|
|
744
|
+
* if the port is closed it is opened automatically. Private sandboxes
|
|
745
|
+
* require the preview token; pass `public: true` for tokenless URLs
|
|
746
|
+
* (required for sandbox deploys).
|
|
747
|
+
*
|
|
748
|
+
* When not attached in this process, the sandbox is looked up by identity
|
|
749
|
+
* (`daytona.get()` does not start it), so other processes can resolve
|
|
750
|
+
* deployments without waking a stopped sandbox.
|
|
751
|
+
*/
|
|
752
|
+
networking = {
|
|
753
|
+
getPortUrl: async (port) => {
|
|
754
|
+
try {
|
|
755
|
+
const sandbox = this._sandbox ?? await this.lookupDetachedSandbox();
|
|
756
|
+
if (!sandbox) return null;
|
|
757
|
+
const preview = await sandbox.getPreviewLink(port);
|
|
758
|
+
return preview?.url ?? null;
|
|
759
|
+
} catch {
|
|
760
|
+
return null;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
};
|
|
741
764
|
status = "pending";
|
|
742
765
|
_daytona = null;
|
|
743
766
|
_sandbox = null;
|
|
@@ -763,6 +786,7 @@ var DaytonaSandbox = class _DaytonaSandbox extends MastraSandbox {
|
|
|
763
786
|
networkBlockAll;
|
|
764
787
|
networkAllowList;
|
|
765
788
|
connectionOpts;
|
|
789
|
+
_constructorOptions;
|
|
766
790
|
constructor(options = {}) {
|
|
767
791
|
super({
|
|
768
792
|
...options,
|
|
@@ -795,10 +819,34 @@ var DaytonaSandbox = class _DaytonaSandbox extends MastraSandbox {
|
|
|
795
819
|
...options.apiUrl !== void 0 && { apiUrl: options.apiUrl },
|
|
796
820
|
...options.target !== void 0 && { target: options.target }
|
|
797
821
|
};
|
|
822
|
+
this._constructorOptions = { ...options };
|
|
798
823
|
}
|
|
799
824
|
generateId() {
|
|
800
825
|
return `daytona-sandbox-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
801
826
|
}
|
|
827
|
+
/**
|
|
828
|
+
* Construct a sibling `DaytonaSandbox` that inherits this sandbox's
|
|
829
|
+
* configuration (credentials, snapshot/image, resources, network policy)
|
|
830
|
+
* with per-instance overrides.
|
|
831
|
+
*
|
|
832
|
+
* Performs no I/O — the sandbox clone provisions (or reconnects to an
|
|
833
|
+
* existing Daytona sandbox with the same logical `id`) on its own `start()`.
|
|
834
|
+
* Use it when one configured sandbox acts as the template for a fleet of
|
|
835
|
+
* independent sandboxes (e.g. one per project).
|
|
836
|
+
*
|
|
837
|
+
* `options.idleTimeoutMinutes` maps to Daytona's `autoStopInterval`
|
|
838
|
+
* (minutes); `options.sandboxId` is ignored because Daytona reconnects by
|
|
839
|
+
* logical `id`.
|
|
840
|
+
*/
|
|
841
|
+
clone(options = {}) {
|
|
842
|
+
const { id: _id, name: _name, ...base } = this._constructorOptions;
|
|
843
|
+
return new _DaytonaSandbox({
|
|
844
|
+
...base,
|
|
845
|
+
...options.id !== void 0 && { id: options.id },
|
|
846
|
+
...options.env !== void 0 && { env: options.env },
|
|
847
|
+
...options.idleTimeoutMinutes !== void 0 && { autoStopInterval: options.idleTimeoutMinutes }
|
|
848
|
+
});
|
|
849
|
+
}
|
|
802
850
|
/**
|
|
803
851
|
* Get the underlying Daytona Sandbox instance for direct access to Daytona APIs.
|
|
804
852
|
*
|
|
@@ -888,13 +936,23 @@ var DaytonaSandbox = class _DaytonaSandbox extends MastraSandbox {
|
|
|
888
936
|
* Unmounts all filesystems, then stops the sandbox.
|
|
889
937
|
*/
|
|
890
938
|
async stop() {
|
|
939
|
+
if (!this._sandbox) {
|
|
940
|
+
try {
|
|
941
|
+
const existing = await this.lookupDetachedSandbox();
|
|
942
|
+
if (existing && existing.state === SandboxState.STARTED) {
|
|
943
|
+
await this._daytona.stop(existing);
|
|
944
|
+
}
|
|
945
|
+
} catch {
|
|
946
|
+
}
|
|
947
|
+
return;
|
|
948
|
+
}
|
|
891
949
|
for (const mountPath of [...this.mounts.entries.keys()]) {
|
|
892
950
|
try {
|
|
893
951
|
await this.unmount(mountPath);
|
|
894
952
|
} catch {
|
|
895
953
|
}
|
|
896
954
|
}
|
|
897
|
-
if (this.
|
|
955
|
+
if (this._daytona) {
|
|
898
956
|
try {
|
|
899
957
|
await this._daytona.stop(this._sandbox);
|
|
900
958
|
} catch {
|
|
@@ -912,14 +970,13 @@ var DaytonaSandbox = class _DaytonaSandbox extends MastraSandbox {
|
|
|
912
970
|
await this._daytona.delete(this._sandbox);
|
|
913
971
|
} catch {
|
|
914
972
|
}
|
|
915
|
-
} else if (!this._sandbox
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
const orphan = await this._daytona.get(lookupKey);
|
|
973
|
+
} else if (!this._sandbox) {
|
|
974
|
+
try {
|
|
975
|
+
const orphan = await this.lookupDetachedSandbox();
|
|
976
|
+
if (orphan) {
|
|
920
977
|
await this._daytona.delete(orphan);
|
|
921
|
-
} catch {
|
|
922
978
|
}
|
|
979
|
+
} catch {
|
|
923
980
|
}
|
|
924
981
|
}
|
|
925
982
|
this._sandbox = null;
|
|
@@ -998,6 +1055,18 @@ var DaytonaSandbox = class _DaytonaSandbox extends MastraSandbox {
|
|
|
998
1055
|
const result = await handle.wait();
|
|
999
1056
|
return { ...result, command, args };
|
|
1000
1057
|
}
|
|
1058
|
+
/**
|
|
1059
|
+
* Bulk-write files into the sandbox filesystem via the SDK's native upload.
|
|
1060
|
+
*/
|
|
1061
|
+
async writeFiles(files) {
|
|
1062
|
+
await this.ensureRunning();
|
|
1063
|
+
await this.daytona.fs.uploadFiles(
|
|
1064
|
+
files.map((file) => ({
|
|
1065
|
+
source: Buffer.isBuffer(file.content) ? file.content : Buffer.from(file.content),
|
|
1066
|
+
destination: file.path
|
|
1067
|
+
}))
|
|
1068
|
+
);
|
|
1069
|
+
}
|
|
1001
1070
|
// ---------------------------------------------------------------------------
|
|
1002
1071
|
// Mount Support
|
|
1003
1072
|
// ---------------------------------------------------------------------------
|
|
@@ -1323,6 +1392,28 @@ var DaytonaSandbox = class _DaytonaSandbox extends MastraSandbox {
|
|
|
1323
1392
|
this.logger.debug(`${LOG_PREFIX} Could not detect working directory, will omit from instructions`);
|
|
1324
1393
|
}
|
|
1325
1394
|
}
|
|
1395
|
+
/**
|
|
1396
|
+
* Look up the existing Daytona sandbox by identity WITHOUT starting it.
|
|
1397
|
+
* Used for detached (cross-process) networking/stop/destroy so those
|
|
1398
|
+
* operations never wake a stopped sandbox. Returns null when not found.
|
|
1399
|
+
*/
|
|
1400
|
+
async lookupDetachedSandbox() {
|
|
1401
|
+
const lookupKey = this._daytonaSandboxId ?? this.sandboxName;
|
|
1402
|
+
if (!lookupKey) {
|
|
1403
|
+
return null;
|
|
1404
|
+
}
|
|
1405
|
+
if (!this._daytona) {
|
|
1406
|
+
this._daytona = new Daytona(this.connectionOpts);
|
|
1407
|
+
}
|
|
1408
|
+
try {
|
|
1409
|
+
return await this._daytona.get(lookupKey);
|
|
1410
|
+
} catch (error) {
|
|
1411
|
+
if (error instanceof DaytonaNotFoundError) {
|
|
1412
|
+
return null;
|
|
1413
|
+
}
|
|
1414
|
+
throw error;
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1326
1417
|
async findExistingSandbox() {
|
|
1327
1418
|
const DEAD_STATES = [
|
|
1328
1419
|
SandboxState.DESTROYED,
|