From 310b358b2a56dd77156fe1a73e8d7ce4c4e56a7e Mon Sep 17 00:00:00 2001 From: Mish Ushakov <10400064+mishushakov@users.noreply.github.com> Date: Thu, 11 Jun 2026 16:48:47 +0200 Subject: [PATCH] feat(sdk): add allowNetworkMounts option to filesystem watch Client-side counterpart to e2b-dev/infra#2982: adds an allowNetworkMounts/allow_network_mounts option to filesystem directory watching across the JS and Python (sync + async) SDKs, allowing watching paths on network filesystem mounts (NFS, CIFS, SMB, FUSE), which are rejected by default. Requires envd 0.6.4 or later; watching with the option against an older sandbox raises a template error. Co-Authored-By: Claude Fable 5 --- .changeset/watch-allow-network-mounts.md | 6 ++ .../src/envd/filesystem/filesystem_pb.ts | 18 +++++- packages/js-sdk/src/envd/versions.ts | 1 + .../js-sdk/src/sandbox/filesystem/index.ts | 22 +++++++ .../js-sdk/tests/sandbox/files/watch.test.ts | 40 +++++++++++++ .../e2b/envd/filesystem/filesystem_pb2.py | 58 +++++++++---------- .../e2b/envd/filesystem/filesystem_pb2.pyi | 10 +++- packages/python-sdk/e2b/envd/versions.py | 1 + .../sandbox_async/filesystem/filesystem.py | 16 ++++- .../e2b/sandbox_sync/filesystem/filesystem.py | 16 ++++- .../async/sandbox_async/files/test_watch.py | 30 ++++++++++ .../sync/sandbox_sync/files/test_watch.py | 28 +++++++++ spec/envd/filesystem/filesystem.proto | 6 ++ 13 files changed, 218 insertions(+), 34 deletions(-) create mode 100644 .changeset/watch-allow-network-mounts.md diff --git a/.changeset/watch-allow-network-mounts.md b/.changeset/watch-allow-network-mounts.md new file mode 100644 index 0000000000..f2dcd593ed --- /dev/null +++ b/.changeset/watch-allow-network-mounts.md @@ -0,0 +1,6 @@ +--- +"@e2b/python-sdk": minor +"e2b": minor +--- + +Add an `allowNetworkMounts`/`allow_network_mounts` option to filesystem directory watching. When enabled, paths on network filesystem mounts (NFS, CIFS, SMB, FUSE) can be watched — they are rejected by default because events on network mounts may be unreliable or not delivered at all. Requires envd 0.6.4 or later; watching with this option against an older sandbox raises a template error. diff --git a/packages/js-sdk/src/envd/filesystem/filesystem_pb.ts b/packages/js-sdk/src/envd/filesystem/filesystem_pb.ts index 1c3bfd94a8..c78f752d0a 100644 --- a/packages/js-sdk/src/envd/filesystem/filesystem_pb.ts +++ b/packages/js-sdk/src/envd/filesystem/filesystem_pb.ts @@ -24,7 +24,7 @@ import type { Message } from '@bufbuild/protobuf' export const file_filesystem_filesystem: GenFile = /*@__PURE__*/ fileDesc( - 'ChtmaWxlc3lzdGVtL2ZpbGVzeXN0ZW0ucHJvdG8SCmZpbGVzeXN0ZW0iMgoLTW92ZVJlcXVlc3QSDgoGc291cmNlGAEgASgJEhMKC2Rlc3RpbmF0aW9uGAIgASgJIjQKDE1vdmVSZXNwb25zZRIkCgVlbnRyeRgBIAEoCzIVLmZpbGVzeXN0ZW0uRW50cnlJbmZvIh4KDk1ha2VEaXJSZXF1ZXN0EgwKBHBhdGgYASABKAkiNwoPTWFrZURpclJlc3BvbnNlEiQKBWVudHJ5GAEgASgLMhUuZmlsZXN5c3RlbS5FbnRyeUluZm8iHQoNUmVtb3ZlUmVxdWVzdBIMCgRwYXRoGAEgASgJIhAKDlJlbW92ZVJlc3BvbnNlIhsKC1N0YXRSZXF1ZXN0EgwKBHBhdGgYASABKAkiNAoMU3RhdFJlc3BvbnNlEiQKBWVudHJ5GAEgASgLMhUuZmlsZXN5c3RlbS5FbnRyeUluZm8i5QIKCUVudHJ5SW5mbxIMCgRuYW1lGAEgASgJEiIKBHR5cGUYAiABKA4yFC5maWxlc3lzdGVtLkZpbGVUeXBlEgwKBHBhdGgYAyABKAkSDAoEc2l6ZRgEIAEoAxIMCgRtb2RlGAUgASgNEhMKC3Blcm1pc3Npb25zGAYgASgJEg0KBW93bmVyGAcgASgJEg0KBWdyb3VwGAggASgJEjEKDW1vZGlmaWVkX3RpbWUYCSABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEhsKDnN5bWxpbmtfdGFyZ2V0GAogASgJSACIAQESNQoIbWV0YWRhdGEYCyADKAsyIy5maWxlc3lzdGVtLkVudHJ5SW5mby5NZXRhZGF0YUVudHJ5Gi8KDU1ldGFkYXRhRW50cnkSCwoDa2V5GAEgASgJEg0KBXZhbHVlGAIgASgJOgI4AUIRCg9fc3ltbGlua190YXJnZXQiLQoOTGlzdERpclJlcXVlc3QSDAoEcGF0aBgBIAEoCRINCgVkZXB0aBgCIAEoDSI5Cg9MaXN0RGlyUmVzcG9uc2USJgoHZW50cmllcxgBIAMoCzIVLmZpbGVzeXN0ZW0uRW50cnlJbmZvIkkKD1dhdGNoRGlyUmVxdWVzdBIMCgRwYXRoGAEgASgJEhEKCXJlY3Vyc2l2ZRgCIAEoCBIVCg1pbmNsdWRlX2VudHJ5GAMgASgIInkKD0ZpbGVzeXN0ZW1FdmVudBIMCgRuYW1lGAEgASgJEiMKBHR5cGUYAiABKA4yFS5maWxlc3lzdGVtLkV2ZW50VHlwZRIpCgVlbnRyeRgDIAEoCzIVLmZpbGVzeXN0ZW0uRW50cnlJbmZvSACIAQFCCAoGX2VudHJ5IuABChBXYXRjaERpclJlc3BvbnNlEjgKBXN0YXJ0GAEgASgLMicuZmlsZXN5c3RlbS5XYXRjaERpclJlc3BvbnNlLlN0YXJ0RXZlbnRIABIxCgpmaWxlc3lzdGVtGAIgASgLMhsuZmlsZXN5c3RlbS5GaWxlc3lzdGVtRXZlbnRIABI7CglrZWVwYWxpdmUYAyABKAsyJi5maWxlc3lzdGVtLldhdGNoRGlyUmVzcG9uc2UuS2VlcEFsaXZlSAAaDAoKU3RhcnRFdmVudBoLCglLZWVwQWxpdmVCBwoFZXZlbnQiTgoUQ3JlYXRlV2F0Y2hlclJlcXVlc3QSDAoEcGF0aBgBIAEoCRIRCglyZWN1cnNpdmUYAiABKAgSFQoNaW5jbHVkZV9lbnRyeRgDIAEoCCIrChVDcmVhdGVXYXRjaGVyUmVzcG9uc2USEgoKd2F0Y2hlcl9pZBgBIAEoCSItChdHZXRXYXRjaGVyRXZlbnRzUmVxdWVzdBISCgp3YXRjaGVyX2lkGAEgASgJIkcKGEdldFdhdGNoZXJFdmVudHNSZXNwb25zZRIrCgZldmVudHMYASADKAsyGy5maWxlc3lzdGVtLkZpbGVzeXN0ZW1FdmVudCIqChRSZW1vdmVXYXRjaGVyUmVxdWVzdBISCgp3YXRjaGVyX2lkGAEgASgJIhcKFVJlbW92ZVdhdGNoZXJSZXNwb25zZSpSCghGaWxlVHlwZRIZChVGSUxFX1RZUEVfVU5TUEVDSUZJRUQQABISCg5GSUxFX1RZUEVfRklMRRABEhcKE0ZJTEVfVFlQRV9ESVJFQ1RPUlkQAiqYAQoJRXZlbnRUeXBlEhoKFkVWRU5UX1RZUEVfVU5TUEVDSUZJRUQQABIVChFFVkVOVF9UWVBFX0NSRUFURRABEhQKEEVWRU5UX1RZUEVfV1JJVEUQAhIVChFFVkVOVF9UWVBFX1JFTU9WRRADEhUKEUVWRU5UX1RZUEVfUkVOQU1FEAQSFAoQRVZFTlRfVFlQRV9DSE1PRBAFMp8FCgpGaWxlc3lzdGVtEjkKBFN0YXQSFy5maWxlc3lzdGVtLlN0YXRSZXF1ZXN0GhguZmlsZXN5c3RlbS5TdGF0UmVzcG9uc2USQgoHTWFrZURpchIaLmZpbGVzeXN0ZW0uTWFrZURpclJlcXVlc3QaGy5maWxlc3lzdGVtLk1ha2VEaXJSZXNwb25zZRI5CgRNb3ZlEhcuZmlsZXN5c3RlbS5Nb3ZlUmVxdWVzdBoYLmZpbGVzeXN0ZW0uTW92ZVJlc3BvbnNlEkIKB0xpc3REaXISGi5maWxlc3lzdGVtLkxpc3REaXJSZXF1ZXN0GhsuZmlsZXN5c3RlbS5MaXN0RGlyUmVzcG9uc2USPwoGUmVtb3ZlEhkuZmlsZXN5c3RlbS5SZW1vdmVSZXF1ZXN0GhouZmlsZXN5c3RlbS5SZW1vdmVSZXNwb25zZRJHCghXYXRjaERpchIbLmZpbGVzeXN0ZW0uV2F0Y2hEaXJSZXF1ZXN0GhwuZmlsZXN5c3RlbS5XYXRjaERpclJlc3BvbnNlMAESVAoNQ3JlYXRlV2F0Y2hlchIgLmZpbGVzeXN0ZW0uQ3JlYXRlV2F0Y2hlclJlcXVlc3QaIS5maWxlc3lzdGVtLkNyZWF0ZVdhdGNoZXJSZXNwb25zZRJdChBHZXRXYXRjaGVyRXZlbnRzEiMuZmlsZXN5c3RlbS5HZXRXYXRjaGVyRXZlbnRzUmVxdWVzdBokLmZpbGVzeXN0ZW0uR2V0V2F0Y2hlckV2ZW50c1Jlc3BvbnNlElQKDVJlbW92ZVdhdGNoZXISIC5maWxlc3lzdGVtLlJlbW92ZVdhdGNoZXJSZXF1ZXN0GiEuZmlsZXN5c3RlbS5SZW1vdmVXYXRjaGVyUmVzcG9uc2VCaQoOY29tLmZpbGVzeXN0ZW1CD0ZpbGVzeXN0ZW1Qcm90b1ABogIDRlhYqgIKRmlsZXN5c3RlbcoCCkZpbGVzeXN0ZW3iAhZGaWxlc3lzdGVtXEdQQk1ldGFkYXRh6gIKRmlsZXN5c3RlbWIGcHJvdG8z', + 'ChtmaWxlc3lzdGVtL2ZpbGVzeXN0ZW0ucHJvdG8SCmZpbGVzeXN0ZW0iMgoLTW92ZVJlcXVlc3QSDgoGc291cmNlGAEgASgJEhMKC2Rlc3RpbmF0aW9uGAIgASgJIjQKDE1vdmVSZXNwb25zZRIkCgVlbnRyeRgBIAEoCzIVLmZpbGVzeXN0ZW0uRW50cnlJbmZvIh4KDk1ha2VEaXJSZXF1ZXN0EgwKBHBhdGgYASABKAkiNwoPTWFrZURpclJlc3BvbnNlEiQKBWVudHJ5GAEgASgLMhUuZmlsZXN5c3RlbS5FbnRyeUluZm8iHQoNUmVtb3ZlUmVxdWVzdBIMCgRwYXRoGAEgASgJIhAKDlJlbW92ZVJlc3BvbnNlIhsKC1N0YXRSZXF1ZXN0EgwKBHBhdGgYASABKAkiNAoMU3RhdFJlc3BvbnNlEiQKBWVudHJ5GAEgASgLMhUuZmlsZXN5c3RlbS5FbnRyeUluZm8i5QIKCUVudHJ5SW5mbxIMCgRuYW1lGAEgASgJEiIKBHR5cGUYAiABKA4yFC5maWxlc3lzdGVtLkZpbGVUeXBlEgwKBHBhdGgYAyABKAkSDAoEc2l6ZRgEIAEoAxIMCgRtb2RlGAUgASgNEhMKC3Blcm1pc3Npb25zGAYgASgJEg0KBW93bmVyGAcgASgJEg0KBWdyb3VwGAggASgJEjEKDW1vZGlmaWVkX3RpbWUYCSABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEhsKDnN5bWxpbmtfdGFyZ2V0GAogASgJSACIAQESNQoIbWV0YWRhdGEYCyADKAsyIy5maWxlc3lzdGVtLkVudHJ5SW5mby5NZXRhZGF0YUVudHJ5Gi8KDU1ldGFkYXRhRW50cnkSCwoDa2V5GAEgASgJEg0KBXZhbHVlGAIgASgJOgI4AUIRCg9fc3ltbGlua190YXJnZXQiLQoOTGlzdERpclJlcXVlc3QSDAoEcGF0aBgBIAEoCRINCgVkZXB0aBgCIAEoDSI5Cg9MaXN0RGlyUmVzcG9uc2USJgoHZW50cmllcxgBIAMoCzIVLmZpbGVzeXN0ZW0uRW50cnlJbmZvImcKD1dhdGNoRGlyUmVxdWVzdBIMCgRwYXRoGAEgASgJEhEKCXJlY3Vyc2l2ZRgCIAEoCBIVCg1pbmNsdWRlX2VudHJ5GAMgASgIEhwKFGFsbG93X25ldHdvcmtfbW91bnRzGAQgASgIInkKD0ZpbGVzeXN0ZW1FdmVudBIMCgRuYW1lGAEgASgJEiMKBHR5cGUYAiABKA4yFS5maWxlc3lzdGVtLkV2ZW50VHlwZRIpCgVlbnRyeRgDIAEoCzIVLmZpbGVzeXN0ZW0uRW50cnlJbmZvSACIAQFCCAoGX2VudHJ5IuABChBXYXRjaERpclJlc3BvbnNlEjgKBXN0YXJ0GAEgASgLMicuZmlsZXN5c3RlbS5XYXRjaERpclJlc3BvbnNlLlN0YXJ0RXZlbnRIABIxCgpmaWxlc3lzdGVtGAIgASgLMhsuZmlsZXN5c3RlbS5GaWxlc3lzdGVtRXZlbnRIABI7CglrZWVwYWxpdmUYAyABKAsyJi5maWxlc3lzdGVtLldhdGNoRGlyUmVzcG9uc2UuS2VlcEFsaXZlSAAaDAoKU3RhcnRFdmVudBoLCglLZWVwQWxpdmVCBwoFZXZlbnQibAoUQ3JlYXRlV2F0Y2hlclJlcXVlc3QSDAoEcGF0aBgBIAEoCRIRCglyZWN1cnNpdmUYAiABKAgSFQoNaW5jbHVkZV9lbnRyeRgDIAEoCBIcChRhbGxvd19uZXR3b3JrX21vdW50cxgEIAEoCCIrChVDcmVhdGVXYXRjaGVyUmVzcG9uc2USEgoKd2F0Y2hlcl9pZBgBIAEoCSItChdHZXRXYXRjaGVyRXZlbnRzUmVxdWVzdBISCgp3YXRjaGVyX2lkGAEgASgJIkcKGEdldFdhdGNoZXJFdmVudHNSZXNwb25zZRIrCgZldmVudHMYASADKAsyGy5maWxlc3lzdGVtLkZpbGVzeXN0ZW1FdmVudCIqChRSZW1vdmVXYXRjaGVyUmVxdWVzdBISCgp3YXRjaGVyX2lkGAEgASgJIhcKFVJlbW92ZVdhdGNoZXJSZXNwb25zZSpSCghGaWxlVHlwZRIZChVGSUxFX1RZUEVfVU5TUEVDSUZJRUQQABISCg5GSUxFX1RZUEVfRklMRRABEhcKE0ZJTEVfVFlQRV9ESVJFQ1RPUlkQAiqYAQoJRXZlbnRUeXBlEhoKFkVWRU5UX1RZUEVfVU5TUEVDSUZJRUQQABIVChFFVkVOVF9UWVBFX0NSRUFURRABEhQKEEVWRU5UX1RZUEVfV1JJVEUQAhIVChFFVkVOVF9UWVBFX1JFTU9WRRADEhUKEUVWRU5UX1RZUEVfUkVOQU1FEAQSFAoQRVZFTlRfVFlQRV9DSE1PRBAFMp8FCgpGaWxlc3lzdGVtEjkKBFN0YXQSFy5maWxlc3lzdGVtLlN0YXRSZXF1ZXN0GhguZmlsZXN5c3RlbS5TdGF0UmVzcG9uc2USQgoHTWFrZURpchIaLmZpbGVzeXN0ZW0uTWFrZURpclJlcXVlc3QaGy5maWxlc3lzdGVtLk1ha2VEaXJSZXNwb25zZRI5CgRNb3ZlEhcuZmlsZXN5c3RlbS5Nb3ZlUmVxdWVzdBoYLmZpbGVzeXN0ZW0uTW92ZVJlc3BvbnNlEkIKB0xpc3REaXISGi5maWxlc3lzdGVtLkxpc3REaXJSZXF1ZXN0GhsuZmlsZXN5c3RlbS5MaXN0RGlyUmVzcG9uc2USPwoGUmVtb3ZlEhkuZmlsZXN5c3RlbS5SZW1vdmVSZXF1ZXN0GhouZmlsZXN5c3RlbS5SZW1vdmVSZXNwb25zZRJHCghXYXRjaERpchIbLmZpbGVzeXN0ZW0uV2F0Y2hEaXJSZXF1ZXN0GhwuZmlsZXN5c3RlbS5XYXRjaERpclJlc3BvbnNlMAESVAoNQ3JlYXRlV2F0Y2hlchIgLmZpbGVzeXN0ZW0uQ3JlYXRlV2F0Y2hlclJlcXVlc3QaIS5maWxlc3lzdGVtLkNyZWF0ZVdhdGNoZXJSZXNwb25zZRJdChBHZXRXYXRjaGVyRXZlbnRzEiMuZmlsZXN5c3RlbS5HZXRXYXRjaGVyRXZlbnRzUmVxdWVzdBokLmZpbGVzeXN0ZW0uR2V0V2F0Y2hlckV2ZW50c1Jlc3BvbnNlElQKDVJlbW92ZVdhdGNoZXISIC5maWxlc3lzdGVtLlJlbW92ZVdhdGNoZXJSZXF1ZXN0GiEuZmlsZXN5c3RlbS5SZW1vdmVXYXRjaGVyUmVzcG9uc2VCaQoOY29tLmZpbGVzeXN0ZW1CD0ZpbGVzeXN0ZW1Qcm90b1ABogIDRlhYqgIKRmlsZXN5c3RlbcoCCkZpbGVzeXN0ZW3iAhZGaWxlc3lzdGVtXEdQQk1ldGFkYXRh6gIKRmlsZXN5c3RlbWIGcHJvdG8z', [file_google_protobuf_timestamp] ) @@ -307,6 +307,14 @@ export type WatchDirRequest = Message<'filesystem.WatchDirRequest'> & { * @generated from field: bool include_entry = 3; */ includeEntry: boolean + + /** + * If true, allows watching paths on network filesystem mounts (NFS, CIFS, SMB, FUSE). + * Events on network mounts may be unreliable or not delivered at all. + * + * @generated from field: bool allow_network_mounts = 4; + */ + allowNetworkMounts: boolean } /** @@ -438,6 +446,14 @@ export type CreateWatcherRequest = * @generated from field: bool include_entry = 3; */ includeEntry: boolean + + /** + * If true, allows watching paths on network filesystem mounts (NFS, CIFS, SMB, FUSE). + * Events on network mounts may be unreliable or not delivered at all. + * + * @generated from field: bool allow_network_mounts = 4; + */ + allowNetworkMounts: boolean } /** diff --git a/packages/js-sdk/src/envd/versions.ts b/packages/js-sdk/src/envd/versions.ts index deba774313..61434e6127 100644 --- a/packages/js-sdk/src/envd/versions.ts +++ b/packages/js-sdk/src/envd/versions.ts @@ -6,3 +6,4 @@ export const ENVD_ENVD_CLOSE = '0.5.2' export const ENVD_OCTET_STREAM_UPLOAD = '0.5.7' export const ENVD_FILE_METADATA = '0.6.2' export const ENVD_VERSION_FS_EVENT_ENTRY_INFO = '0.6.3' +export const ENVD_VERSION_WATCH_NETWORK_MOUNTS = '0.6.4' diff --git a/packages/js-sdk/src/sandbox/filesystem/index.ts b/packages/js-sdk/src/sandbox/filesystem/index.ts index d6dd5f8957..e63e9ce3b1 100644 --- a/packages/js-sdk/src/sandbox/filesystem/index.ts +++ b/packages/js-sdk/src/sandbox/filesystem/index.ts @@ -35,6 +35,7 @@ import { ENVD_OCTET_STREAM_UPLOAD, ENVD_VERSION_FS_EVENT_ENTRY_INFO, ENVD_VERSION_RECURSIVE_WATCH, + ENVD_VERSION_WATCH_NETWORK_MOUNTS, } from '../../envd/versions' import { FileNotFoundError, @@ -311,6 +312,15 @@ export interface WatchOpts extends FilesystemRequestOpts { * throws a `TemplateError`. */ includeEntry?: boolean + /** + * Allow watching paths on network filesystem mounts (NFS, CIFS, SMB, FUSE), + * which are rejected by default. Events on network mounts may be unreliable + * or not delivered at all. + * + * Requires envd 0.6.4 or later. Watching with this option against an older sandbox + * throws a `TemplateError`. + */ + allowNetworkMounts?: boolean } /** @@ -909,6 +919,17 @@ export class Filesystem { ) } + if ( + opts?.allowNetworkMounts && + this.envdApi.version && + compareVersions(this.envdApi.version, ENVD_VERSION_WATCH_NETWORK_MOUNTS) < + 0 + ) { + throw new TemplateError( + 'You need to update the template to watch directories on network mounts.' + ) + } + const requestTimeoutMs = opts?.requestTimeoutMs ?? this.connectionConfig.requestTimeoutMs @@ -922,6 +943,7 @@ export class Filesystem { path, recursive: opts?.recursive ?? this.defaultWatchRecursive, includeEntry: opts?.includeEntry ?? false, + allowNetworkMounts: opts?.allowNetworkMounts ?? false, }, { headers: { diff --git a/packages/js-sdk/tests/sandbox/files/watch.test.ts b/packages/js-sdk/tests/sandbox/files/watch.test.ts index 7251242221..850d4a4a6a 100644 --- a/packages/js-sdk/tests/sandbox/files/watch.test.ts +++ b/packages/js-sdk/tests/sandbox/files/watch.test.ts @@ -177,6 +177,46 @@ sandboxTest('watch directory changes with entry info', async ({ sandbox }) => { await handle.stop() }) +sandboxTest( + 'watch directory changes with network mounts allowed', + async ({ sandbox }) => { + const dirname = 'test_watch_dir_network_mounts' + const filename = 'test_watch.txt' + const content = 'This file will be watched.' + const newContent = 'This file has been modified.' + + await sandbox.files.makeDir(dirname) + await sandbox.files.write(`${dirname}/${filename}`, content) + + let trigger: () => void + + const eventPromise = new Promise((resolve) => { + trigger = resolve + }) + + // The flag only lifts the network-mount restriction — watching a regular + // directory must work the same with it enabled. + const handle = await sandbox.files.watchDir( + dirname, + async (event) => { + if ( + event.type === FilesystemEventType.WRITE && + event.name === filename + ) { + trigger() + } + }, + { allowNetworkMounts: true } + ) + + await sandbox.files.write(`${dirname}/${filename}`, newContent) + + await eventPromise + + await handle.stop() + } +) + sandboxTest('watch non-existing directory', async ({ sandbox }) => { const dirname = 'non_existing_watch_dir' diff --git a/packages/python-sdk/e2b/envd/filesystem/filesystem_pb2.py b/packages/python-sdk/e2b/envd/filesystem/filesystem_pb2.py index 0f3a927123..f2a37967a9 100644 --- a/packages/python-sdk/e2b/envd/filesystem/filesystem_pb2.py +++ b/packages/python-sdk/e2b/envd/filesystem/filesystem_pb2.py @@ -15,7 +15,7 @@ from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1b\x66ilesystem/filesystem.proto\x12\nfilesystem\x1a\x1fgoogle/protobuf/timestamp.proto\"G\n\x0bMoveRequest\x12\x16\n\x06source\x18\x01 \x01(\tR\x06source\x12 \n\x0b\x64\x65stination\x18\x02 \x01(\tR\x0b\x64\x65stination\";\n\x0cMoveResponse\x12+\n\x05\x65ntry\x18\x01 \x01(\x0b\x32\x15.filesystem.EntryInfoR\x05\x65ntry\"$\n\x0eMakeDirRequest\x12\x12\n\x04path\x18\x01 \x01(\tR\x04path\">\n\x0fMakeDirResponse\x12+\n\x05\x65ntry\x18\x01 \x01(\x0b\x32\x15.filesystem.EntryInfoR\x05\x65ntry\"#\n\rRemoveRequest\x12\x12\n\x04path\x18\x01 \x01(\tR\x04path\"\x10\n\x0eRemoveResponse\"!\n\x0bStatRequest\x12\x12\n\x04path\x18\x01 \x01(\tR\x04path\";\n\x0cStatResponse\x12+\n\x05\x65ntry\x18\x01 \x01(\x0b\x32\x15.filesystem.EntryInfoR\x05\x65ntry\"\xd1\x03\n\tEntryInfo\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12(\n\x04type\x18\x02 \x01(\x0e\x32\x14.filesystem.FileTypeR\x04type\x12\x12\n\x04path\x18\x03 \x01(\tR\x04path\x12\x12\n\x04size\x18\x04 \x01(\x03R\x04size\x12\x12\n\x04mode\x18\x05 \x01(\rR\x04mode\x12 \n\x0bpermissions\x18\x06 \x01(\tR\x0bpermissions\x12\x14\n\x05owner\x18\x07 \x01(\tR\x05owner\x12\x14\n\x05group\x18\x08 \x01(\tR\x05group\x12?\n\rmodified_time\x18\t \x01(\x0b\x32\x1a.google.protobuf.TimestampR\x0cmodifiedTime\x12*\n\x0esymlink_target\x18\n \x01(\tH\x00R\rsymlinkTarget\x88\x01\x01\x12?\n\x08metadata\x18\x0b \x03(\x0b\x32#.filesystem.EntryInfo.MetadataEntryR\x08metadata\x1a;\n\rMetadataEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01\x42\x11\n\x0f_symlink_target\":\n\x0eListDirRequest\x12\x12\n\x04path\x18\x01 \x01(\tR\x04path\x12\x14\n\x05\x64\x65pth\x18\x02 \x01(\rR\x05\x64\x65pth\"B\n\x0fListDirResponse\x12/\n\x07\x65ntries\x18\x01 \x03(\x0b\x32\x15.filesystem.EntryInfoR\x07\x65ntries\"h\n\x0fWatchDirRequest\x12\x12\n\x04path\x18\x01 \x01(\tR\x04path\x12\x1c\n\trecursive\x18\x02 \x01(\x08R\trecursive\x12#\n\rinclude_entry\x18\x03 \x01(\x08R\x0cincludeEntry\"\x8c\x01\n\x0f\x46ilesystemEvent\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12)\n\x04type\x18\x02 \x01(\x0e\x32\x15.filesystem.EventTypeR\x04type\x12\x30\n\x05\x65ntry\x18\x03 \x01(\x0b\x32\x15.filesystem.EntryInfoH\x00R\x05\x65ntry\x88\x01\x01\x42\x08\n\x06_entry\"\xfe\x01\n\x10WatchDirResponse\x12?\n\x05start\x18\x01 \x01(\x0b\x32\'.filesystem.WatchDirResponse.StartEventH\x00R\x05start\x12=\n\nfilesystem\x18\x02 \x01(\x0b\x32\x1b.filesystem.FilesystemEventH\x00R\nfilesystem\x12\x46\n\tkeepalive\x18\x03 \x01(\x0b\x32&.filesystem.WatchDirResponse.KeepAliveH\x00R\tkeepalive\x1a\x0c\n\nStartEvent\x1a\x0b\n\tKeepAliveB\x07\n\x05\x65vent\"m\n\x14\x43reateWatcherRequest\x12\x12\n\x04path\x18\x01 \x01(\tR\x04path\x12\x1c\n\trecursive\x18\x02 \x01(\x08R\trecursive\x12#\n\rinclude_entry\x18\x03 \x01(\x08R\x0cincludeEntry\"6\n\x15\x43reateWatcherResponse\x12\x1d\n\nwatcher_id\x18\x01 \x01(\tR\twatcherId\"8\n\x17GetWatcherEventsRequest\x12\x1d\n\nwatcher_id\x18\x01 \x01(\tR\twatcherId\"O\n\x18GetWatcherEventsResponse\x12\x33\n\x06\x65vents\x18\x01 \x03(\x0b\x32\x1b.filesystem.FilesystemEventR\x06\x65vents\"5\n\x14RemoveWatcherRequest\x12\x1d\n\nwatcher_id\x18\x01 \x01(\tR\twatcherId\"\x17\n\x15RemoveWatcherResponse*R\n\x08\x46ileType\x12\x19\n\x15\x46ILE_TYPE_UNSPECIFIED\x10\x00\x12\x12\n\x0e\x46ILE_TYPE_FILE\x10\x01\x12\x17\n\x13\x46ILE_TYPE_DIRECTORY\x10\x02*\x98\x01\n\tEventType\x12\x1a\n\x16\x45VENT_TYPE_UNSPECIFIED\x10\x00\x12\x15\n\x11\x45VENT_TYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENT_TYPE_WRITE\x10\x02\x12\x15\n\x11\x45VENT_TYPE_REMOVE\x10\x03\x12\x15\n\x11\x45VENT_TYPE_RENAME\x10\x04\x12\x14\n\x10\x45VENT_TYPE_CHMOD\x10\x05\x32\x9f\x05\n\nFilesystem\x12\x39\n\x04Stat\x12\x17.filesystem.StatRequest\x1a\x18.filesystem.StatResponse\x12\x42\n\x07MakeDir\x12\x1a.filesystem.MakeDirRequest\x1a\x1b.filesystem.MakeDirResponse\x12\x39\n\x04Move\x12\x17.filesystem.MoveRequest\x1a\x18.filesystem.MoveResponse\x12\x42\n\x07ListDir\x12\x1a.filesystem.ListDirRequest\x1a\x1b.filesystem.ListDirResponse\x12?\n\x06Remove\x12\x19.filesystem.RemoveRequest\x1a\x1a.filesystem.RemoveResponse\x12G\n\x08WatchDir\x12\x1b.filesystem.WatchDirRequest\x1a\x1c.filesystem.WatchDirResponse0\x01\x12T\n\rCreateWatcher\x12 .filesystem.CreateWatcherRequest\x1a!.filesystem.CreateWatcherResponse\x12]\n\x10GetWatcherEvents\x12#.filesystem.GetWatcherEventsRequest\x1a$.filesystem.GetWatcherEventsResponse\x12T\n\rRemoveWatcher\x12 .filesystem.RemoveWatcherRequest\x1a!.filesystem.RemoveWatcherResponseBi\n\x0e\x63om.filesystemB\x0f\x46ilesystemProtoP\x01\xa2\x02\x03\x46XX\xaa\x02\nFilesystem\xca\x02\nFilesystem\xe2\x02\x16\x46ilesystem\\GPBMetadata\xea\x02\nFilesystemb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1b\x66ilesystem/filesystem.proto\x12\nfilesystem\x1a\x1fgoogle/protobuf/timestamp.proto\"G\n\x0bMoveRequest\x12\x16\n\x06source\x18\x01 \x01(\tR\x06source\x12 \n\x0b\x64\x65stination\x18\x02 \x01(\tR\x0b\x64\x65stination\";\n\x0cMoveResponse\x12+\n\x05\x65ntry\x18\x01 \x01(\x0b\x32\x15.filesystem.EntryInfoR\x05\x65ntry\"$\n\x0eMakeDirRequest\x12\x12\n\x04path\x18\x01 \x01(\tR\x04path\">\n\x0fMakeDirResponse\x12+\n\x05\x65ntry\x18\x01 \x01(\x0b\x32\x15.filesystem.EntryInfoR\x05\x65ntry\"#\n\rRemoveRequest\x12\x12\n\x04path\x18\x01 \x01(\tR\x04path\"\x10\n\x0eRemoveResponse\"!\n\x0bStatRequest\x12\x12\n\x04path\x18\x01 \x01(\tR\x04path\";\n\x0cStatResponse\x12+\n\x05\x65ntry\x18\x01 \x01(\x0b\x32\x15.filesystem.EntryInfoR\x05\x65ntry\"\xd1\x03\n\tEntryInfo\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12(\n\x04type\x18\x02 \x01(\x0e\x32\x14.filesystem.FileTypeR\x04type\x12\x12\n\x04path\x18\x03 \x01(\tR\x04path\x12\x12\n\x04size\x18\x04 \x01(\x03R\x04size\x12\x12\n\x04mode\x18\x05 \x01(\rR\x04mode\x12 \n\x0bpermissions\x18\x06 \x01(\tR\x0bpermissions\x12\x14\n\x05owner\x18\x07 \x01(\tR\x05owner\x12\x14\n\x05group\x18\x08 \x01(\tR\x05group\x12?\n\rmodified_time\x18\t \x01(\x0b\x32\x1a.google.protobuf.TimestampR\x0cmodifiedTime\x12*\n\x0esymlink_target\x18\n \x01(\tH\x00R\rsymlinkTarget\x88\x01\x01\x12?\n\x08metadata\x18\x0b \x03(\x0b\x32#.filesystem.EntryInfo.MetadataEntryR\x08metadata\x1a;\n\rMetadataEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01\x42\x11\n\x0f_symlink_target\":\n\x0eListDirRequest\x12\x12\n\x04path\x18\x01 \x01(\tR\x04path\x12\x14\n\x05\x64\x65pth\x18\x02 \x01(\rR\x05\x64\x65pth\"B\n\x0fListDirResponse\x12/\n\x07\x65ntries\x18\x01 \x03(\x0b\x32\x15.filesystem.EntryInfoR\x07\x65ntries\"\x9a\x01\n\x0fWatchDirRequest\x12\x12\n\x04path\x18\x01 \x01(\tR\x04path\x12\x1c\n\trecursive\x18\x02 \x01(\x08R\trecursive\x12#\n\rinclude_entry\x18\x03 \x01(\x08R\x0cincludeEntry\x12\x30\n\x14\x61llow_network_mounts\x18\x04 \x01(\x08R\x12\x61llowNetworkMounts\"\x8c\x01\n\x0f\x46ilesystemEvent\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12)\n\x04type\x18\x02 \x01(\x0e\x32\x15.filesystem.EventTypeR\x04type\x12\x30\n\x05\x65ntry\x18\x03 \x01(\x0b\x32\x15.filesystem.EntryInfoH\x00R\x05\x65ntry\x88\x01\x01\x42\x08\n\x06_entry\"\xfe\x01\n\x10WatchDirResponse\x12?\n\x05start\x18\x01 \x01(\x0b\x32\'.filesystem.WatchDirResponse.StartEventH\x00R\x05start\x12=\n\nfilesystem\x18\x02 \x01(\x0b\x32\x1b.filesystem.FilesystemEventH\x00R\nfilesystem\x12\x46\n\tkeepalive\x18\x03 \x01(\x0b\x32&.filesystem.WatchDirResponse.KeepAliveH\x00R\tkeepalive\x1a\x0c\n\nStartEvent\x1a\x0b\n\tKeepAliveB\x07\n\x05\x65vent\"\x9f\x01\n\x14\x43reateWatcherRequest\x12\x12\n\x04path\x18\x01 \x01(\tR\x04path\x12\x1c\n\trecursive\x18\x02 \x01(\x08R\trecursive\x12#\n\rinclude_entry\x18\x03 \x01(\x08R\x0cincludeEntry\x12\x30\n\x14\x61llow_network_mounts\x18\x04 \x01(\x08R\x12\x61llowNetworkMounts\"6\n\x15\x43reateWatcherResponse\x12\x1d\n\nwatcher_id\x18\x01 \x01(\tR\twatcherId\"8\n\x17GetWatcherEventsRequest\x12\x1d\n\nwatcher_id\x18\x01 \x01(\tR\twatcherId\"O\n\x18GetWatcherEventsResponse\x12\x33\n\x06\x65vents\x18\x01 \x03(\x0b\x32\x1b.filesystem.FilesystemEventR\x06\x65vents\"5\n\x14RemoveWatcherRequest\x12\x1d\n\nwatcher_id\x18\x01 \x01(\tR\twatcherId\"\x17\n\x15RemoveWatcherResponse*R\n\x08\x46ileType\x12\x19\n\x15\x46ILE_TYPE_UNSPECIFIED\x10\x00\x12\x12\n\x0e\x46ILE_TYPE_FILE\x10\x01\x12\x17\n\x13\x46ILE_TYPE_DIRECTORY\x10\x02*\x98\x01\n\tEventType\x12\x1a\n\x16\x45VENT_TYPE_UNSPECIFIED\x10\x00\x12\x15\n\x11\x45VENT_TYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENT_TYPE_WRITE\x10\x02\x12\x15\n\x11\x45VENT_TYPE_REMOVE\x10\x03\x12\x15\n\x11\x45VENT_TYPE_RENAME\x10\x04\x12\x14\n\x10\x45VENT_TYPE_CHMOD\x10\x05\x32\x9f\x05\n\nFilesystem\x12\x39\n\x04Stat\x12\x17.filesystem.StatRequest\x1a\x18.filesystem.StatResponse\x12\x42\n\x07MakeDir\x12\x1a.filesystem.MakeDirRequest\x1a\x1b.filesystem.MakeDirResponse\x12\x39\n\x04Move\x12\x17.filesystem.MoveRequest\x1a\x18.filesystem.MoveResponse\x12\x42\n\x07ListDir\x12\x1a.filesystem.ListDirRequest\x1a\x1b.filesystem.ListDirResponse\x12?\n\x06Remove\x12\x19.filesystem.RemoveRequest\x1a\x1a.filesystem.RemoveResponse\x12G\n\x08WatchDir\x12\x1b.filesystem.WatchDirRequest\x1a\x1c.filesystem.WatchDirResponse0\x01\x12T\n\rCreateWatcher\x12 .filesystem.CreateWatcherRequest\x1a!.filesystem.CreateWatcherResponse\x12]\n\x10GetWatcherEvents\x12#.filesystem.GetWatcherEventsRequest\x1a$.filesystem.GetWatcherEventsResponse\x12T\n\rRemoveWatcher\x12 .filesystem.RemoveWatcherRequest\x1a!.filesystem.RemoveWatcherResponseBi\n\x0e\x63om.filesystemB\x0f\x46ilesystemProtoP\x01\xa2\x02\x03\x46XX\xaa\x02\nFilesystem\xca\x02\nFilesystem\xe2\x02\x16\x46ilesystem\\GPBMetadata\xea\x02\nFilesystemb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -25,10 +25,10 @@ _globals['DESCRIPTOR']._serialized_options = b'\n\016com.filesystemB\017FilesystemProtoP\001\242\002\003FXX\252\002\nFilesystem\312\002\nFilesystem\342\002\026Filesystem\\GPBMetadata\352\002\nFilesystem' _globals['_ENTRYINFO_METADATAENTRY']._loaded_options = None _globals['_ENTRYINFO_METADATAENTRY']._serialized_options = b'8\001' - _globals['_FILETYPE']._serialized_start=1951 - _globals['_FILETYPE']._serialized_end=2033 - _globals['_EVENTTYPE']._serialized_start=2036 - _globals['_EVENTTYPE']._serialized_end=2188 + _globals['_FILETYPE']._serialized_start=2053 + _globals['_FILETYPE']._serialized_end=2135 + _globals['_EVENTTYPE']._serialized_start=2138 + _globals['_EVENTTYPE']._serialized_end=2290 _globals['_MOVEREQUEST']._serialized_start=76 _globals['_MOVEREQUEST']._serialized_end=147 _globals['_MOVERESPONSE']._serialized_start=149 @@ -53,28 +53,28 @@ _globals['_LISTDIRREQUEST']._serialized_end=989 _globals['_LISTDIRRESPONSE']._serialized_start=991 _globals['_LISTDIRRESPONSE']._serialized_end=1057 - _globals['_WATCHDIRREQUEST']._serialized_start=1059 - _globals['_WATCHDIRREQUEST']._serialized_end=1163 - _globals['_FILESYSTEMEVENT']._serialized_start=1166 - _globals['_FILESYSTEMEVENT']._serialized_end=1306 - _globals['_WATCHDIRRESPONSE']._serialized_start=1309 - _globals['_WATCHDIRRESPONSE']._serialized_end=1563 - _globals['_WATCHDIRRESPONSE_STARTEVENT']._serialized_start=1529 - _globals['_WATCHDIRRESPONSE_STARTEVENT']._serialized_end=1541 - _globals['_WATCHDIRRESPONSE_KEEPALIVE']._serialized_start=1543 - _globals['_WATCHDIRRESPONSE_KEEPALIVE']._serialized_end=1554 - _globals['_CREATEWATCHERREQUEST']._serialized_start=1565 - _globals['_CREATEWATCHERREQUEST']._serialized_end=1674 - _globals['_CREATEWATCHERRESPONSE']._serialized_start=1676 - _globals['_CREATEWATCHERRESPONSE']._serialized_end=1730 - _globals['_GETWATCHEREVENTSREQUEST']._serialized_start=1732 - _globals['_GETWATCHEREVENTSREQUEST']._serialized_end=1788 - _globals['_GETWATCHEREVENTSRESPONSE']._serialized_start=1790 - _globals['_GETWATCHEREVENTSRESPONSE']._serialized_end=1869 - _globals['_REMOVEWATCHERREQUEST']._serialized_start=1871 - _globals['_REMOVEWATCHERREQUEST']._serialized_end=1924 - _globals['_REMOVEWATCHERRESPONSE']._serialized_start=1926 - _globals['_REMOVEWATCHERRESPONSE']._serialized_end=1949 - _globals['_FILESYSTEM']._serialized_start=2191 - _globals['_FILESYSTEM']._serialized_end=2862 + _globals['_WATCHDIRREQUEST']._serialized_start=1060 + _globals['_WATCHDIRREQUEST']._serialized_end=1214 + _globals['_FILESYSTEMEVENT']._serialized_start=1217 + _globals['_FILESYSTEMEVENT']._serialized_end=1357 + _globals['_WATCHDIRRESPONSE']._serialized_start=1360 + _globals['_WATCHDIRRESPONSE']._serialized_end=1614 + _globals['_WATCHDIRRESPONSE_STARTEVENT']._serialized_start=1580 + _globals['_WATCHDIRRESPONSE_STARTEVENT']._serialized_end=1592 + _globals['_WATCHDIRRESPONSE_KEEPALIVE']._serialized_start=1594 + _globals['_WATCHDIRRESPONSE_KEEPALIVE']._serialized_end=1605 + _globals['_CREATEWATCHERREQUEST']._serialized_start=1617 + _globals['_CREATEWATCHERREQUEST']._serialized_end=1776 + _globals['_CREATEWATCHERRESPONSE']._serialized_start=1778 + _globals['_CREATEWATCHERRESPONSE']._serialized_end=1832 + _globals['_GETWATCHEREVENTSREQUEST']._serialized_start=1834 + _globals['_GETWATCHEREVENTSREQUEST']._serialized_end=1890 + _globals['_GETWATCHEREVENTSRESPONSE']._serialized_start=1892 + _globals['_GETWATCHEREVENTSRESPONSE']._serialized_end=1971 + _globals['_REMOVEWATCHERREQUEST']._serialized_start=1973 + _globals['_REMOVEWATCHERREQUEST']._serialized_end=2026 + _globals['_REMOVEWATCHERRESPONSE']._serialized_start=2028 + _globals['_REMOVEWATCHERRESPONSE']._serialized_end=2051 + _globals['_FILESYSTEM']._serialized_start=2293 + _globals['_FILESYSTEM']._serialized_end=2964 # @@protoc_insertion_point(module_scope) diff --git a/packages/python-sdk/e2b/envd/filesystem/filesystem_pb2.pyi b/packages/python-sdk/e2b/envd/filesystem/filesystem_pb2.pyi index 1346ada824..73ede13a15 100644 --- a/packages/python-sdk/e2b/envd/filesystem/filesystem_pb2.pyi +++ b/packages/python-sdk/e2b/envd/filesystem/filesystem_pb2.pyi @@ -168,18 +168,21 @@ class ListDirResponse(_message.Message): ) -> None: ... class WatchDirRequest(_message.Message): - __slots__ = ("path", "recursive", "include_entry") + __slots__ = ("path", "recursive", "include_entry", "allow_network_mounts") PATH_FIELD_NUMBER: _ClassVar[int] RECURSIVE_FIELD_NUMBER: _ClassVar[int] INCLUDE_ENTRY_FIELD_NUMBER: _ClassVar[int] + ALLOW_NETWORK_MOUNTS_FIELD_NUMBER: _ClassVar[int] path: str recursive: bool include_entry: bool + allow_network_mounts: bool def __init__( self, path: _Optional[str] = ..., recursive: bool = ..., include_entry: bool = ..., + allow_network_mounts: bool = ..., ) -> None: ... class FilesystemEvent(_message.Message): @@ -221,18 +224,21 @@ class WatchDirResponse(_message.Message): ) -> None: ... class CreateWatcherRequest(_message.Message): - __slots__ = ("path", "recursive", "include_entry") + __slots__ = ("path", "recursive", "include_entry", "allow_network_mounts") PATH_FIELD_NUMBER: _ClassVar[int] RECURSIVE_FIELD_NUMBER: _ClassVar[int] INCLUDE_ENTRY_FIELD_NUMBER: _ClassVar[int] + ALLOW_NETWORK_MOUNTS_FIELD_NUMBER: _ClassVar[int] path: str recursive: bool include_entry: bool + allow_network_mounts: bool def __init__( self, path: _Optional[str] = ..., recursive: bool = ..., include_entry: bool = ..., + allow_network_mounts: bool = ..., ) -> None: ... class CreateWatcherResponse(_message.Message): diff --git a/packages/python-sdk/e2b/envd/versions.py b/packages/python-sdk/e2b/envd/versions.py index 0827e99a37..2b4542ce24 100644 --- a/packages/python-sdk/e2b/envd/versions.py +++ b/packages/python-sdk/e2b/envd/versions.py @@ -8,3 +8,4 @@ ENVD_OCTET_STREAM_UPLOAD = Version("0.5.7") ENVD_FILE_METADATA = Version("0.6.2") ENVD_VERSION_FS_EVENT_ENTRY_INFO = Version("0.6.3") +ENVD_VERSION_WATCH_NETWORK_MOUNTS = Version("0.6.4") diff --git a/packages/python-sdk/e2b/sandbox_async/filesystem/filesystem.py b/packages/python-sdk/e2b/sandbox_async/filesystem/filesystem.py index be1b993972..6c88811b09 100644 --- a/packages/python-sdk/e2b/sandbox_async/filesystem/filesystem.py +++ b/packages/python-sdk/e2b/sandbox_async/filesystem/filesystem.py @@ -23,6 +23,7 @@ ENVD_OCTET_STREAM_UPLOAD, ENVD_VERSION_FS_EVENT_ENTRY_INFO, ENVD_VERSION_RECURSIVE_WATCH, + ENVD_VERSION_WATCH_NETWORK_MOUNTS, ) from e2b.exceptions import ( FileNotFoundException, @@ -554,6 +555,7 @@ async def watch_dir( timeout: Optional[float] = 60, recursive: bool = False, include_entry: bool = False, + allow_network_mounts: bool = False, ) -> AsyncWatchHandle: """ Watch directory for filesystem events. @@ -566,6 +568,7 @@ async def watch_dir( :param timeout: Timeout for the watch operation in **seconds**. Using `0` will not limit the watch time :param recursive: Watch directory recursively :param include_entry: Include the `EntryInfo` of the affected entry in each event, when available. Requires envd 0.6.3 or later + :param allow_network_mounts: Allow watching paths on network filesystem mounts (NFS, CIFS, SMB, FUSE), which are rejected by default. Events on network mounts may be unreliable or not delivered at all. Requires envd 0.6.4 or later :return: `AsyncWatchHandle` object for stopping watching directory """ @@ -579,9 +582,20 @@ async def watch_dir( "You need to update the template to include entry info in watch events." ) + if ( + allow_network_mounts + and self._envd_version < ENVD_VERSION_WATCH_NETWORK_MOUNTS + ): + raise TemplateException( + "You need to update the template to watch directories on network mounts." + ) + events = self._rpc.awatch_dir( filesystem_pb2.WatchDirRequest( - path=path, recursive=recursive, include_entry=include_entry + path=path, + recursive=recursive, + include_entry=include_entry, + allow_network_mounts=allow_network_mounts, ), request_timeout=self._connection_config.get_request_timeout( request_timeout diff --git a/packages/python-sdk/e2b/sandbox_sync/filesystem/filesystem.py b/packages/python-sdk/e2b/sandbox_sync/filesystem/filesystem.py index 1544929f3f..7014e43284 100644 --- a/packages/python-sdk/e2b/sandbox_sync/filesystem/filesystem.py +++ b/packages/python-sdk/e2b/sandbox_sync/filesystem/filesystem.py @@ -23,6 +23,7 @@ ENVD_OCTET_STREAM_UPLOAD, ENVD_VERSION_FS_EVENT_ENTRY_INFO, ENVD_VERSION_RECURSIVE_WATCH, + ENVD_VERSION_WATCH_NETWORK_MOUNTS, ) from e2b.exceptions import ( FileNotFoundException, @@ -541,6 +542,7 @@ def watch_dir( request_timeout: Optional[float] = None, recursive: bool = False, include_entry: bool = False, + allow_network_mounts: bool = False, ) -> WatchHandle: """ Watch directory for filesystem events. @@ -550,6 +552,7 @@ def watch_dir( :param request_timeout: Timeout for the request in **seconds** :param recursive: Watch directory recursively :param include_entry: Include the `EntryInfo` of the affected entry in each event, when available. Requires envd 0.6.3 or later + :param allow_network_mounts: Allow watching paths on network filesystem mounts (NFS, CIFS, SMB, FUSE), which are rejected by default. Events on network mounts may be unreliable or not delivered at all. Requires envd 0.6.4 or later :return: `WatchHandle` object for stopping watching directory """ @@ -563,10 +566,21 @@ def watch_dir( "You need to update the template to include entry info in watch events." ) + if ( + allow_network_mounts + and self._envd_version < ENVD_VERSION_WATCH_NETWORK_MOUNTS + ): + raise TemplateException( + "You need to update the template to watch directories on network mounts." + ) + try: r = self._rpc.create_watcher( filesystem_pb2.CreateWatcherRequest( - path=path, recursive=recursive, include_entry=include_entry + path=path, + recursive=recursive, + include_entry=include_entry, + allow_network_mounts=allow_network_mounts, ), request_timeout=self._connection_config.get_request_timeout( request_timeout diff --git a/packages/python-sdk/tests/async/sandbox_async/files/test_watch.py b/packages/python-sdk/tests/async/sandbox_async/files/test_watch.py index bf2730a17b..a55a9f462c 100644 --- a/packages/python-sdk/tests/async/sandbox_async/files/test_watch.py +++ b/packages/python-sdk/tests/async/sandbox_async/files/test_watch.py @@ -47,6 +47,36 @@ def handle_event(e: FilesystemEvent): await handle.stop() +async def test_watch_directory_changes_with_network_mounts_allowed( + async_sandbox: AsyncSandbox, +): + dirname = "test_watch_dir_network_mounts" + filename = "test_watch.txt" + content = "This file will be watched." + new_content = "This file has been modified." + + await async_sandbox.files.make_dir(dirname) + await async_sandbox.files.write(f"{dirname}/{filename}", content) + + event_triggered = Event() + + def handle_event(e: FilesystemEvent): + if e.type == FilesystemEventType.WRITE and e.name == filename: + event_triggered.set() + + # The flag only lifts the network-mount restriction — watching a regular + # directory must work the same with it enabled. + handle = await async_sandbox.files.watch_dir( + dirname, on_event=handle_event, allow_network_mounts=True + ) + + await async_sandbox.files.write(f"{dirname}/{filename}", new_content) + + await event_triggered.wait() + + await handle.stop() + + async def test_watch_directory_changes(async_sandbox: AsyncSandbox): dirname = "test_watch_dir" filename = "test_watch.txt" diff --git a/packages/python-sdk/tests/sync/sandbox_sync/files/test_watch.py b/packages/python-sdk/tests/sync/sandbox_sync/files/test_watch.py index 11900dac66..9159070fa7 100644 --- a/packages/python-sdk/tests/sync/sandbox_sync/files/test_watch.py +++ b/packages/python-sdk/tests/sync/sandbox_sync/files/test_watch.py @@ -40,6 +40,34 @@ def test_watch_directory_changes_with_entry_info(sandbox: Sandbox): handle.stop() +def test_watch_directory_changes_with_network_mounts_allowed(sandbox: Sandbox): + dirname = "test_watch_dir_network_mounts" + filename = "test_watch.txt" + content = "This file will be watched." + new_content = "This file has been modified." + + sandbox.files.make_dir(dirname) + sandbox.files.write(f"{dirname}/{filename}", content) + + # The flag only lifts the network-mount restriction — watching a regular + # directory must work the same with it enabled. + handle = sandbox.files.watch_dir(dirname, allow_network_mounts=True) + sandbox.files.write(f"{dirname}/{filename}", new_content) + + events = handle.get_new_events() + write_event = None + for event in events: + if event.type == FilesystemEventType.WRITE and event.name == filename: + write_event = event + break + + assert write_event is not None, ( + f"Expected WRITE event for {filename}, but got events: {events}" + ) + + handle.stop() + + def test_watch_directory_changes(sandbox: Sandbox): dirname = "test_watch_dir" filename = "test_watch.txt" diff --git a/spec/envd/filesystem/filesystem.proto b/spec/envd/filesystem/filesystem.proto index ad9d18a57d..e97b687daf 100644 --- a/spec/envd/filesystem/filesystem.proto +++ b/spec/envd/filesystem/filesystem.proto @@ -88,6 +88,9 @@ message WatchDirRequest { bool recursive = 2; // If true, each FilesystemEvent includes the EntryInfo of the affected entry, when available. bool include_entry = 3; + // If true, allows watching paths on network filesystem mounts (NFS, CIFS, SMB, FUSE). + // Events on network mounts may be unreliable or not delivered at all. + bool allow_network_mounts = 4; } message FilesystemEvent { @@ -116,6 +119,9 @@ message CreateWatcherRequest { bool recursive = 2; // If true, each FilesystemEvent includes the EntryInfo of the affected entry, when available. bool include_entry = 3; + // If true, allows watching paths on network filesystem mounts (NFS, CIFS, SMB, FUSE). + // Events on network mounts may be unreliable or not delivered at all. + bool allow_network_mounts = 4; } message CreateWatcherResponse {