codecraftai-interop-vmworkstation 1.2.0__py3-none-any.whl

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.
@@ -0,0 +1,49 @@
1
+ """
2
+ .. module:: vmworkstation
3
+ :platform: Darwin, Linux, Unix, Windows
4
+ :synopsis: Public exports for the VMware Workstation interop client.
5
+
6
+ .. moduleauthor:: Myron Walker <myron.walker@gmail.com>
7
+ """
8
+
9
+ __author__ = "Myron Walker"
10
+ __copyright__ = "Copyright 2026, Code Craft AI LLC"
11
+ __credits__ = []
12
+
13
+
14
+ from ccai.interop.vmworkstation.aspectsvmworkstation import (
15
+ AspectsVMWorkstation,
16
+ DEFAULT_VMWORKSTATION_ASPECTS,
17
+ )
18
+ from ccai.interop.vmworkstation.exceptions import (
19
+ VMWorkstationError,
20
+ VMRunCommandError,
21
+ VMWorkstationParseError,
22
+ )
23
+ from ccai.interop.vmworkstation.guestcredential import GuestCredential
24
+ from ccai.interop.vmworkstation.redaction import redact_argv
25
+ from ccai.interop.vmworkstation.results import (
26
+ GuestExecResult,
27
+ ToolsState,
28
+ VMWorkstationActionResult,
29
+ VMWorkstationActionStatus,
30
+ )
31
+ from ccai.interop.vmworkstation.snapshots import SnapshotRecord
32
+ from ccai.interop.vmworkstation.vmworkstationclient import VMWorkstationClient
33
+
34
+
35
+ __all__ = [
36
+ "AspectsVMWorkstation",
37
+ "DEFAULT_VMWORKSTATION_ASPECTS",
38
+ "GuestCredential",
39
+ "GuestExecResult",
40
+ "redact_argv",
41
+ "SnapshotRecord",
42
+ "ToolsState",
43
+ "VMRunCommandError",
44
+ "VMWorkstationActionResult",
45
+ "VMWorkstationActionStatus",
46
+ "VMWorkstationClient",
47
+ "VMWorkstationError",
48
+ "VMWorkstationParseError",
49
+ ]
@@ -0,0 +1,48 @@
1
+ """
2
+ .. module:: aspectsvmworkstation
3
+ :platform: Darwin, Linux, Unix, Windows
4
+ :synopsis: Behaviour-modifying parameters for the VMware Workstation
5
+ interop client.
6
+
7
+ .. moduleauthor:: Myron Walker <myron.walker@gmail.com>
8
+ """
9
+
10
+ __author__ = "Myron Walker"
11
+ __copyright__ = "Copyright 2026, Code Craft AI LLC"
12
+ __credits__ = []
13
+
14
+
15
+ from dataclasses import dataclass
16
+ from typing import Optional
17
+
18
+ from ccai.aspects.aspects import Aspects
19
+
20
+
21
+ @dataclass
22
+ class AspectsVMWorkstation(Aspects):
23
+ """
24
+ Workstation-specific behaviour carrier.
25
+
26
+ Inherits the generic completion / timeout / attempt fields from
27
+ :class:`Aspects` and adds the small set of behaviour controls the
28
+ ``vmrun`` dispatcher honours.
29
+
30
+ :param elevated: When True, the runner is invoked with ``elevated``.
31
+ :param raise_on_failure: When True, non-zero ``vmrun`` exits raise a
32
+ :class:`VMRunCommandError` instead of returning a ``FAILED``
33
+ result.
34
+ :param command_timeout: Per-invocation timeout passed through to the
35
+ runner. ``None`` means no enforced timeout at the runner.
36
+ """
37
+ elevated: bool = False
38
+ raise_on_failure: bool = False
39
+ command_timeout: Optional[float] = None
40
+
41
+
42
+ DEFAULT_VMWORKSTATION_ASPECTS = AspectsVMWorkstation()
43
+
44
+
45
+ __all__ = [
46
+ "AspectsVMWorkstation",
47
+ "DEFAULT_VMWORKSTATION_ASPECTS",
48
+ ]
@@ -0,0 +1,79 @@
1
+ """
2
+ .. module:: exceptions
3
+ :platform: Darwin, Linux, Unix, Windows
4
+ :synopsis: Typed exceptions raised by the VMware Workstation interop
5
+ client.
6
+
7
+ .. moduleauthor:: Myron Walker <myron.walker@gmail.com>
8
+ """
9
+
10
+ __author__ = "Myron Walker"
11
+ __copyright__ = "Copyright 2026, Code Craft AI LLC"
12
+ __credits__ = []
13
+
14
+
15
+ from typing import Optional
16
+
17
+
18
+ class VMWorkstationError(Exception):
19
+ """
20
+ Base class for all VMware Workstation interop errors.
21
+ """
22
+
23
+
24
+ class VMRunCommandError(VMWorkstationError):
25
+ """
26
+ Raised when a ``vmrun`` invocation exits non-zero and the caller has
27
+ opted into exception-on-failure behaviour.
28
+
29
+ :param message: Human-readable failure description.
30
+ :param exit_code: The ``vmrun`` exit code, when available.
31
+ :param stdout: Captured stdout, when available.
32
+ :param stderr: Captured stderr, when available.
33
+ :param command: The argv that was dispatched, when available.
34
+ """
35
+
36
+ def __init__(
37
+ self,
38
+ *,
39
+ message: str,
40
+ exit_code: Optional[int],
41
+ stdout: Optional[str],
42
+ stderr: Optional[str],
43
+ command: Optional[list],
44
+ ) -> None:
45
+ super().__init__(message)
46
+ self.message = message
47
+ self.exit_code = exit_code
48
+ self.stdout = stdout
49
+ self.stderr = stderr
50
+ self.command = command
51
+ return
52
+
53
+
54
+ class VMWorkstationParseError(VMWorkstationError):
55
+ """
56
+ Raised when ``vmrun`` output cannot be parsed into the expected
57
+ structure.
58
+
59
+ :param message: Human-readable description of the parse failure.
60
+ :param raw_output: The raw output that could not be parsed.
61
+ """
62
+
63
+ def __init__(
64
+ self,
65
+ *,
66
+ message: str,
67
+ raw_output: Optional[str],
68
+ ) -> None:
69
+ super().__init__(message)
70
+ self.message = message
71
+ self.raw_output = raw_output
72
+ return
73
+
74
+
75
+ __all__ = [
76
+ "VMRunCommandError",
77
+ "VMWorkstationError",
78
+ "VMWorkstationParseError",
79
+ ]
@@ -0,0 +1,31 @@
1
+ """
2
+ .. module:: guestcredential
3
+ :platform: Darwin, Linux, Unix, Windows
4
+ :synopsis: Guest credential carrier for ``vmrun`` guest-exec verbs.
5
+
6
+ .. moduleauthor:: Myron Walker <myron.walker@gmail.com>
7
+ """
8
+
9
+ __author__ = "Myron Walker"
10
+ __copyright__ = "Copyright 2026, Code Craft AI LLC"
11
+ __credits__ = []
12
+
13
+
14
+ from dataclasses import dataclass
15
+
16
+
17
+ @dataclass
18
+ class GuestCredential:
19
+ """
20
+ Username/password pair passed to ``vmrun -gu/-gp`` for guest-exec
21
+ verbs (``runProgramInGuest``, ``copyFileFromHostToGuest``,
22
+ ``copyFileFromGuestToHost``).
23
+
24
+ :param username: Guest OS username.
25
+ :param password: Guest OS password.
26
+ """
27
+ username: str
28
+ password: str
29
+
30
+
31
+ __all__ = ["GuestCredential"]
File without changes
@@ -0,0 +1,62 @@
1
+ """
2
+ .. module:: redaction
3
+ :platform: Darwin, Linux, Unix, Windows
4
+ :synopsis: Argv redaction helpers for the VMware Workstation interop
5
+ client.
6
+
7
+ The ``vmrun`` guest-exec subcommands accept the guest password on the
8
+ command line via the ``-gp`` flag. The real argv must carry the
9
+ plaintext password so the command can actually execute, but any copy of
10
+ that argv that is stored on a result object, embedded in an exception,
11
+ or written to a log must have the secret scrubbed.
12
+
13
+ .. moduleauthor:: Myron Walker <myron.walker@gmail.com>
14
+ """
15
+
16
+ __author__ = "Myron Walker"
17
+ __copyright__ = "Copyright 2026, Code Craft AI LLC"
18
+ __credits__ = []
19
+
20
+
21
+ from typing import List, Optional
22
+
23
+
24
+ _REDACTED_PLACEHOLDER = "<redacted>"
25
+ _SECRET_FLAGS = ("-gp",)
26
+
27
+
28
+ def redact_argv(*, argv: Optional[List[str]]) -> Optional[List[str]]:
29
+ """
30
+ Return a copy of ``argv`` with the value following any secret flag
31
+ (currently ``-gp``, the guest password) replaced by a redaction
32
+ placeholder.
33
+
34
+ The original list is never mutated; the real argv used for dispatch
35
+ is left intact.
36
+
37
+ :param argv: The argv to redact, or ``None``.
38
+
39
+ :returns: A redacted copy of ``argv``, or ``None`` when ``argv`` is
40
+ ``None``.
41
+ """
42
+ if argv is None:
43
+ rtnval: Optional[List[str]] = None
44
+ return rtnval
45
+
46
+ redacted: List[str] = []
47
+ redact_next = False
48
+ for token in argv:
49
+ if redact_next is True:
50
+ redacted.append(_REDACTED_PLACEHOLDER)
51
+ redact_next = False
52
+ continue
53
+
54
+ redacted.append(token)
55
+ if token in _SECRET_FLAGS:
56
+ redact_next = True
57
+
58
+ rtnval = redacted
59
+ return rtnval
60
+
61
+
62
+ __all__ = ["redact_argv"]
@@ -0,0 +1,81 @@
1
+ """
2
+ .. module:: results
3
+ :platform: Darwin, Linux, Unix, Windows
4
+ :synopsis: Result types for the VMware Workstation interop client.
5
+
6
+ .. moduleauthor:: Myron Walker <myron.walker@gmail.com>
7
+ """
8
+
9
+ __author__ = "Myron Walker"
10
+ __copyright__ = "Copyright 2026, Code Craft AI LLC"
11
+ __credits__ = []
12
+
13
+
14
+ from dataclasses import dataclass
15
+ from enum import Enum
16
+ from typing import List, Optional
17
+
18
+
19
+ class VMWorkstationActionStatus(Enum):
20
+ """
21
+ Status enumeration for ``vmrun`` action results.
22
+ """
23
+ SUCCESS = "success"
24
+ FAILED = "failed"
25
+ DRY_RUN = "dry_run"
26
+
27
+
28
+ class ToolsState(Enum):
29
+ """
30
+ VMware Tools state as reported by ``vmrun checkToolsState``.
31
+ """
32
+ INSTALLED = "installed"
33
+ RUNNING = "running"
34
+ NOT_INSTALLED = "not_installed"
35
+ UNKNOWN = "unknown"
36
+
37
+
38
+ @dataclass
39
+ class VMWorkstationActionResult:
40
+ """
41
+ Result of a ``vmrun`` action invocation.
42
+
43
+ :param status: The outcome status.
44
+ :param detail: Free-form description of the action context.
45
+ :param exit_code: The ``vmrun`` exit code, when available.
46
+ :param stdout: Captured stdout, when available.
47
+ :param stderr: Captured stderr, when available.
48
+ :param command: The argv that was dispatched, when available.
49
+ """
50
+ status: VMWorkstationActionStatus
51
+ detail: Optional[str]
52
+ exit_code: Optional[int]
53
+ stdout: Optional[str]
54
+ stderr: Optional[str]
55
+ command: Optional[List[str]]
56
+
57
+
58
+ @dataclass
59
+ class GuestExecResult:
60
+ """
61
+ Result of a guest-exec ``vmrun`` invocation.
62
+
63
+ :param status: The outcome status.
64
+ :param exit_code: The ``vmrun`` exit code, when available.
65
+ :param stdout: Captured stdout, when available.
66
+ :param stderr: Captured stderr, when available.
67
+ :param command: The argv that was dispatched, when available.
68
+ """
69
+ status: VMWorkstationActionStatus
70
+ exit_code: Optional[int]
71
+ stdout: Optional[str]
72
+ stderr: Optional[str]
73
+ command: Optional[List[str]]
74
+
75
+
76
+ __all__ = [
77
+ "GuestExecResult",
78
+ "ToolsState",
79
+ "VMWorkstationActionResult",
80
+ "VMWorkstationActionStatus",
81
+ ]
@@ -0,0 +1,180 @@
1
+ """
2
+ .. module:: snapshots
3
+ :platform: Darwin, Linux, Unix, Windows
4
+ :synopsis: ``vmrun listSnapshots`` parsing and the
5
+ :class:`SnapshotRecord` data class.
6
+
7
+ .. moduleauthor:: Myron Walker <myron.walker@gmail.com>
8
+ """
9
+
10
+ __author__ = "Myron Walker"
11
+ __copyright__ = "Copyright 2026, Code Craft AI LLC"
12
+ __credits__ = []
13
+
14
+
15
+ import os
16
+
17
+ from dataclasses import dataclass
18
+ from typing import List, Optional
19
+
20
+ from ccai.interop.vmworkstation.exceptions import VMWorkstationParseError
21
+
22
+
23
+ @dataclass
24
+ class SnapshotRecord:
25
+ """
26
+ Record describing one snapshot reported by ``vmrun listSnapshots``.
27
+
28
+ :param name: The snapshot name (as printed by ``vmrun``).
29
+ :param parent: The parent snapshot name, or ``None`` for top-level.
30
+ """
31
+ name: str
32
+ parent: Optional[str]
33
+
34
+
35
+ def parse_list_snapshots(*, payload: str) -> List[SnapshotRecord]:
36
+ """
37
+ Parse the stdout of ``vmrun listSnapshots <vmx>``.
38
+
39
+ ``vmrun listSnapshots`` typically prints::
40
+
41
+ Total snapshots: <n>
42
+ <name1>
43
+ <name2>
44
+ ...
45
+
46
+ Snapshot trees are presented as flat names; parent linkage is not
47
+ available from the ``listSnapshots`` plain form, so :attr:`parent`
48
+ is always ``None``.
49
+
50
+ :param payload: The decoded stdout from ``vmrun``.
51
+
52
+ :returns: A list of :class:`SnapshotRecord`, possibly empty.
53
+ :raises VMWorkstationParseError: When the header line cannot be
54
+ located or the declared count does not match the body.
55
+ """
56
+ if payload is None:
57
+ text = ""
58
+ else:
59
+ text = payload
60
+
61
+ raw_lines = text.splitlines()
62
+ stripped_lines: List[str] = []
63
+ for raw in raw_lines:
64
+ stripped = raw.strip()
65
+ if len(stripped) > 0:
66
+ stripped_lines.append(stripped)
67
+
68
+ if len(stripped_lines) == 0:
69
+ rtnval: List[SnapshotRecord] = []
70
+ return rtnval
71
+
72
+ header = stripped_lines[0]
73
+ lower_header = header.lower()
74
+ if lower_header.startswith("total snapshots:") is False:
75
+ err_msg_lines = ["Unexpected 'vmrun listSnapshots' header."]
76
+ err_msg_lines.append(" HEADER: {!r}".format(header))
77
+ err_msg = os.linesep.join(err_msg_lines)
78
+ raise VMWorkstationParseError(message=err_msg, raw_output=text)
79
+
80
+ count_text = header.split(":", 1)[1].strip()
81
+ try:
82
+ declared_count = int(count_text)
83
+ except ValueError:
84
+ err_msg_lines = ["Unable to parse snapshot count."]
85
+ err_msg_lines.append(" COUNT_TEXT: {!r}".format(count_text))
86
+ err_msg = os.linesep.join(err_msg_lines)
87
+ raise VMWorkstationParseError(
88
+ message=err_msg, raw_output=text,
89
+ ) from None
90
+
91
+ body_lines = stripped_lines[1:]
92
+ if len(body_lines) != declared_count:
93
+ err_msg_lines = [
94
+ "Snapshot count mismatch between header and body.",
95
+ ]
96
+ err_msg_lines.append(" DECLARED: {}".format(declared_count))
97
+ err_msg_lines.append(" ACTUAL: {}".format(len(body_lines)))
98
+ err_msg = os.linesep.join(err_msg_lines)
99
+ raise VMWorkstationParseError(message=err_msg, raw_output=text)
100
+
101
+ records: List[SnapshotRecord] = []
102
+ for body in body_lines:
103
+ record = SnapshotRecord(name=body, parent=None)
104
+ records.append(record)
105
+
106
+ rtnval = records
107
+ return rtnval
108
+
109
+
110
+ def parse_list_running(*, payload: str) -> List[str]:
111
+ """
112
+ Parse the stdout of ``vmrun list``.
113
+
114
+ ``vmrun list`` prints::
115
+
116
+ Total running VMs: <n>
117
+ <vmx_path_1>
118
+ <vmx_path_2>
119
+ ...
120
+
121
+ :param payload: The decoded stdout from ``vmrun``.
122
+
123
+ :returns: A list of VMX paths, possibly empty.
124
+ :raises VMWorkstationParseError: When the header line cannot be
125
+ located or the declared count does not match the body.
126
+ """
127
+ if payload is None:
128
+ text = ""
129
+ else:
130
+ text = payload
131
+
132
+ raw_lines = text.splitlines()
133
+ stripped_lines: List[str] = []
134
+ for raw in raw_lines:
135
+ stripped = raw.strip()
136
+ if len(stripped) > 0:
137
+ stripped_lines.append(stripped)
138
+
139
+ if len(stripped_lines) == 0:
140
+ rtnval: List[str] = []
141
+ return rtnval
142
+
143
+ header = stripped_lines[0]
144
+ lower_header = header.lower()
145
+ if lower_header.startswith("total running vms:") is False:
146
+ err_msg_lines = ["Unexpected 'vmrun list' header."]
147
+ err_msg_lines.append(" HEADER: {!r}".format(header))
148
+ err_msg = os.linesep.join(err_msg_lines)
149
+ raise VMWorkstationParseError(message=err_msg, raw_output=text)
150
+
151
+ count_text = header.split(":", 1)[1].strip()
152
+ try:
153
+ declared_count = int(count_text)
154
+ except ValueError:
155
+ err_msg_lines = ["Unable to parse running VM count."]
156
+ err_msg_lines.append(" COUNT_TEXT: {!r}".format(count_text))
157
+ err_msg = os.linesep.join(err_msg_lines)
158
+ raise VMWorkstationParseError(
159
+ message=err_msg, raw_output=text,
160
+ ) from None
161
+
162
+ body_lines = stripped_lines[1:]
163
+ if len(body_lines) != declared_count:
164
+ err_msg_lines = [
165
+ "Running VM count mismatch between header and body.",
166
+ ]
167
+ err_msg_lines.append(" DECLARED: {}".format(declared_count))
168
+ err_msg_lines.append(" ACTUAL: {}".format(len(body_lines)))
169
+ err_msg = os.linesep.join(err_msg_lines)
170
+ raise VMWorkstationParseError(message=err_msg, raw_output=text)
171
+
172
+ rtnval = list(body_lines)
173
+ return rtnval
174
+
175
+
176
+ __all__ = [
177
+ "SnapshotRecord",
178
+ "parse_list_running",
179
+ "parse_list_snapshots",
180
+ ]