fleet-python 0.2.0__py3-none-any.whl → 0.2.2__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.
Potentially problematic release.
This version of fleet-python might be problematic. Click here for more details.
- examples/dsl_example.py +112 -0
- examples/example.py +38 -0
- examples/nova_act_example.py +180 -0
- examples/openai_example.py +448 -0
- examples/quickstart.py +5 -5
- fleet/__init__.py +24 -3
- fleet/base.py +1 -1
- fleet/client.py +60 -28
- fleet/env/__init__.py +2 -7
- fleet/env/client.py +9 -235
- fleet/manager/__init__.py +22 -0
- fleet/manager/client.py +258 -0
- fleet/{env → manager}/models.py +15 -14
- fleet/resources/base.py +5 -2
- fleet/resources/browser.py +32 -6
- fleet/resources/sqlite.py +5 -5
- fleet/verifiers/__init__.py +4 -0
- fleet/verifiers/database_snapshot.py +666 -0
- fleet/verifiers/sql_differ.py +187 -0
- {fleet_python-0.2.0.dist-info → fleet_python-0.2.2.dist-info}/METADATA +1 -1
- fleet_python-0.2.2.dist-info/RECORD +27 -0
- examples/browser_control_example.py +0 -51
- fleet_python-0.2.0.dist-info/RECORD +0 -19
- /fleet/{env → manager}/base.py +0 -0
- {fleet_python-0.2.0.dist-info → fleet_python-0.2.2.dist-info}/WHEEL +0 -0
- {fleet_python-0.2.0.dist-info → fleet_python-0.2.2.dist-info}/licenses/LICENSE +0 -0
- {fleet_python-0.2.0.dist-info → fleet_python-0.2.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import sqlite3
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class SQLiteDiffer:
|
|
6
|
+
def __init__(self, before_db: str, after_db: str):
|
|
7
|
+
self.before_db = before_db
|
|
8
|
+
self.after_db = after_db
|
|
9
|
+
|
|
10
|
+
def get_table_schema(self, db_path: str, table_name: str) -> list[str]:
|
|
11
|
+
"""Get column names for a table"""
|
|
12
|
+
conn = sqlite3.connect(db_path)
|
|
13
|
+
cursor = conn.cursor()
|
|
14
|
+
cursor.execute(f"PRAGMA table_info({table_name})")
|
|
15
|
+
columns = [row[1] for row in cursor.fetchall()]
|
|
16
|
+
conn.close()
|
|
17
|
+
return columns
|
|
18
|
+
|
|
19
|
+
def get_primary_key_columns(self, db_path: str, table_name: str) -> list[str]:
|
|
20
|
+
"""Get all primary key columns for a table, ordered by their position"""
|
|
21
|
+
conn = sqlite3.connect(db_path)
|
|
22
|
+
cursor = conn.cursor()
|
|
23
|
+
cursor.execute(f"PRAGMA table_info({table_name})")
|
|
24
|
+
|
|
25
|
+
pk_columns = []
|
|
26
|
+
for row in cursor.fetchall():
|
|
27
|
+
# row format: (cid, name, type, notnull, dflt_value, pk)
|
|
28
|
+
if row[5] > 0: # pk > 0 means it's part of primary key
|
|
29
|
+
pk_columns.append((row[5], row[1])) # (pk_position, column_name)
|
|
30
|
+
|
|
31
|
+
conn.close()
|
|
32
|
+
|
|
33
|
+
# Sort by primary key position and return just the column names
|
|
34
|
+
pk_columns.sort(key=lambda x: x[0])
|
|
35
|
+
return [col[1] for col in pk_columns]
|
|
36
|
+
|
|
37
|
+
def get_all_tables(self, db_path: str) -> list[str]:
|
|
38
|
+
"""Get all table names from database"""
|
|
39
|
+
conn = sqlite3.connect(db_path)
|
|
40
|
+
cursor = conn.cursor()
|
|
41
|
+
cursor.execute(
|
|
42
|
+
"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'"
|
|
43
|
+
)
|
|
44
|
+
tables = [row[0] for row in cursor.fetchall()]
|
|
45
|
+
conn.close()
|
|
46
|
+
return tables
|
|
47
|
+
|
|
48
|
+
def get_table_data(
|
|
49
|
+
self,
|
|
50
|
+
db_path: str,
|
|
51
|
+
table_name: str,
|
|
52
|
+
primary_key_columns: list[str] | None = None,
|
|
53
|
+
) -> tuple[dict[Any, dict], list[str]]:
|
|
54
|
+
"""Get table data indexed by primary key (single column or composite)"""
|
|
55
|
+
conn = sqlite3.connect(db_path)
|
|
56
|
+
conn.row_factory = sqlite3.Row
|
|
57
|
+
cursor = conn.cursor()
|
|
58
|
+
|
|
59
|
+
# If no primary key specified, try to detect it
|
|
60
|
+
if primary_key_columns is None:
|
|
61
|
+
primary_key_columns = self.get_primary_key_columns(db_path, table_name)
|
|
62
|
+
|
|
63
|
+
# Fallback strategies if no primary key found
|
|
64
|
+
if not primary_key_columns:
|
|
65
|
+
columns = self.get_table_schema(db_path, table_name)
|
|
66
|
+
if "id" in columns:
|
|
67
|
+
primary_key_columns = ["id"]
|
|
68
|
+
else:
|
|
69
|
+
primary_key_columns = ["rowid"]
|
|
70
|
+
|
|
71
|
+
cursor.execute(f"SELECT rowid, * FROM {table_name}")
|
|
72
|
+
rows = cursor.fetchall()
|
|
73
|
+
|
|
74
|
+
data = {}
|
|
75
|
+
for row in rows:
|
|
76
|
+
row_dict = dict(row)
|
|
77
|
+
|
|
78
|
+
# Create primary key value (single value or tuple for composite keys)
|
|
79
|
+
if len(primary_key_columns) == 1:
|
|
80
|
+
pk_col = primary_key_columns[0]
|
|
81
|
+
if pk_col == "rowid":
|
|
82
|
+
pk_value = row_dict["rowid"]
|
|
83
|
+
else:
|
|
84
|
+
pk_value = row_dict.get(pk_col)
|
|
85
|
+
else:
|
|
86
|
+
# Composite primary key - use tuple of values
|
|
87
|
+
pk_values = []
|
|
88
|
+
for pk_col in primary_key_columns:
|
|
89
|
+
pk_values.append(row_dict.get(pk_col))
|
|
90
|
+
pk_value = tuple(pk_values)
|
|
91
|
+
|
|
92
|
+
if pk_value is not None and (
|
|
93
|
+
not isinstance(pk_value, tuple) or all(v is not None for v in pk_value)
|
|
94
|
+
):
|
|
95
|
+
data[pk_value] = row_dict
|
|
96
|
+
|
|
97
|
+
conn.close()
|
|
98
|
+
return data, primary_key_columns
|
|
99
|
+
|
|
100
|
+
def compare_rows(self, before_row: dict, after_row: dict) -> dict[str, dict]:
|
|
101
|
+
"""Compare two rows field by field"""
|
|
102
|
+
changes = {}
|
|
103
|
+
|
|
104
|
+
all_fields = set(before_row.keys()) | set(after_row.keys())
|
|
105
|
+
|
|
106
|
+
for field in all_fields:
|
|
107
|
+
before_val = before_row.get(field)
|
|
108
|
+
after_val = after_row.get(field)
|
|
109
|
+
|
|
110
|
+
if before_val != after_val:
|
|
111
|
+
changes[field] = {"before": before_val, "after": after_val}
|
|
112
|
+
|
|
113
|
+
return changes
|
|
114
|
+
|
|
115
|
+
def diff_table(
|
|
116
|
+
self, table_name: str, primary_key_columns: list[str] | None = None
|
|
117
|
+
) -> dict:
|
|
118
|
+
"""Create comprehensive diff of a table"""
|
|
119
|
+
before_data, detected_pk = self.get_table_data(
|
|
120
|
+
self.before_db, table_name, primary_key_columns
|
|
121
|
+
)
|
|
122
|
+
after_data, _ = self.get_table_data(
|
|
123
|
+
self.after_db, table_name, primary_key_columns or detected_pk
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
before_keys = set(before_data.keys())
|
|
127
|
+
after_keys = set(after_data.keys())
|
|
128
|
+
|
|
129
|
+
# Find different types of changes
|
|
130
|
+
added_keys = after_keys - before_keys
|
|
131
|
+
removed_keys = before_keys - after_keys
|
|
132
|
+
common_keys = before_keys & after_keys
|
|
133
|
+
|
|
134
|
+
result = {
|
|
135
|
+
"table_name": table_name,
|
|
136
|
+
"primary_key": primary_key_columns or detected_pk,
|
|
137
|
+
"added_rows": [],
|
|
138
|
+
"removed_rows": [],
|
|
139
|
+
"modified_rows": [],
|
|
140
|
+
"unchanged_count": 0,
|
|
141
|
+
"total_changes": 0,
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
# Added rows
|
|
145
|
+
for key in added_keys:
|
|
146
|
+
result["added_rows"].append({"row_id": key, "data": after_data[key]})
|
|
147
|
+
|
|
148
|
+
# Removed rows
|
|
149
|
+
for key in removed_keys:
|
|
150
|
+
result["removed_rows"].append({"row_id": key, "data": before_data[key]})
|
|
151
|
+
|
|
152
|
+
# Check for modifications in existing rows
|
|
153
|
+
for key in common_keys:
|
|
154
|
+
field_changes = self.compare_rows(before_data[key], after_data[key])
|
|
155
|
+
|
|
156
|
+
if field_changes:
|
|
157
|
+
result["modified_rows"].append(
|
|
158
|
+
{
|
|
159
|
+
"row_id": key,
|
|
160
|
+
"changes": field_changes,
|
|
161
|
+
"before_row": before_data[key],
|
|
162
|
+
"after_row": after_data[key],
|
|
163
|
+
}
|
|
164
|
+
)
|
|
165
|
+
else:
|
|
166
|
+
result["unchanged_count"] += 1
|
|
167
|
+
|
|
168
|
+
result["total_changes"] = (
|
|
169
|
+
len(result["added_rows"])
|
|
170
|
+
+ len(result["removed_rows"])
|
|
171
|
+
+ len(result["modified_rows"])
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
return result
|
|
175
|
+
|
|
176
|
+
def diff_all_tables(self) -> dict:
|
|
177
|
+
"""Diff all tables in the database"""
|
|
178
|
+
tables = self.get_all_tables(self.before_db)
|
|
179
|
+
results = {}
|
|
180
|
+
|
|
181
|
+
for table in tables:
|
|
182
|
+
try:
|
|
183
|
+
results[table] = self.diff_table(table)
|
|
184
|
+
except Exception as e:
|
|
185
|
+
results[table] = {"error": str(e)}
|
|
186
|
+
|
|
187
|
+
return results
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
examples/dsl_example.py,sha256=LKqm2f2BHZh3Z3MWhpi4L87l5qt2TR0Ol1Mdb4mQ3bA,3297
|
|
2
|
+
examples/example.py,sha256=G_UC4fD55QKPPMAKINtsJs0U6b0wg-GbpaeIGwLnhWw,933
|
|
3
|
+
examples/nova_act_example.py,sha256=Z-LtSpAP4QCRg4zh-e-MQw5L8bdfZ6ev3fB5ExYToao,6443
|
|
4
|
+
examples/openai_example.py,sha256=BWiDQrQCcyKgveVBXmyD7V2xypoPnumF5T5WvuOCpJ0,15258
|
|
5
|
+
examples/quickstart.py,sha256=m5i5gJ0RdHJgBUlOeUSkLPHjj2GVhNvm7BP6g_A9mzA,4691
|
|
6
|
+
fleet/__init__.py,sha256=D8aNOSGc-kUGrgPTVGtRhmz_Lov5yyqSX9-iBQv6Y4A,1361
|
|
7
|
+
fleet/base.py,sha256=5JnzMMyT1Ey8SrYs4Ydka3bUoYRRA-wxHx1yCURAM48,2009
|
|
8
|
+
fleet/client.py,sha256=5QKsCSmVhR10lcEIky5heL2NFZbmSvej19VXtfVuq14,6980
|
|
9
|
+
fleet/exceptions.py,sha256=yG3QWprCw1OnF-vdFBFJWE4m3ftBLBng31Dr__VbjI4,2249
|
|
10
|
+
fleet/models.py,sha256=Jf6Zmk689TPXhTSnVENK_VCw0VsujWzEWsN3T29MQ0k,3713
|
|
11
|
+
fleet/env/__init__.py,sha256=_zcEf-sukzVblsTfzQbc9ixDC47bsTts2fHlKyu3OMc,81
|
|
12
|
+
fleet/env/client.py,sha256=I2ja8upwdRcBnehn4GRtkRgWPCLz0cJpbSvQmSuKVrc,423
|
|
13
|
+
fleet/manager/__init__.py,sha256=KXs3cz8_524XwG-MwizeEwsaX65HL8ueZieXFb7RItw,467
|
|
14
|
+
fleet/manager/base.py,sha256=bm6BGd81TnTDqE_qG6S50Xhikf9DwNqEEk1uKFY7dEk,1540
|
|
15
|
+
fleet/manager/client.py,sha256=s0EZcFolQyrTscbWVnvcVJpovMmWQFBR-NOBNFjgSBY,8571
|
|
16
|
+
fleet/manager/models.py,sha256=oSWZ893tRhPTLyPybrk1c5AOYduWEGIKEGZjUQa1vGw,3910
|
|
17
|
+
fleet/resources/base.py,sha256=eFo3wnzfPt6UoMxZSLdklvnPTDgqvjS870u8RcOEAvU,666
|
|
18
|
+
fleet/resources/browser.py,sha256=H8wgabP4lxSoN2q3iFQ35qYU3nwSG-gOVQcQ8ot3DqU,1522
|
|
19
|
+
fleet/resources/sqlite.py,sha256=Y30EcelvRGip0jyFEr9wxt3_EfwWS_Edt3O_qiha-pU,1498
|
|
20
|
+
fleet/verifiers/__init__.py,sha256=CLw9-6bshvX7PgLcQ5KYCG9hmCLx83pi3ZS4FZPauAU,163
|
|
21
|
+
fleet/verifiers/database_snapshot.py,sha256=8-DkYVEvHCwBEhKKAfMJBQmE2146z2aZvIaRtZRCk1s,26147
|
|
22
|
+
fleet/verifiers/sql_differ.py,sha256=4o6Gt9472x8rcbhA58OMXdaczJiWjpYwR7LeB32D4QE,6489
|
|
23
|
+
fleet_python-0.2.2.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
24
|
+
fleet_python-0.2.2.dist-info/METADATA,sha256=qwWJ_66gEeJeWSDcQt8SGkCNJfB0-6Y6dqFXyDDRu-0,3069
|
|
25
|
+
fleet_python-0.2.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
26
|
+
fleet_python-0.2.2.dist-info/top_level.txt,sha256=AOyXOrBXUjPcH4BumElz_D95kiWKNIpUbUPFP_9gCLk,15
|
|
27
|
+
fleet_python-0.2.2.dist-info/RECORD,,
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""Example demonstrating browser control with Fleet Manager Client."""
|
|
3
|
-
|
|
4
|
-
import asyncio
|
|
5
|
-
import fleet as flt
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
async def main():
|
|
9
|
-
fleet = flt.AsyncFleet()
|
|
10
|
-
|
|
11
|
-
environments = await fleet.list_envs()
|
|
12
|
-
print("Environments:", len(environments))
|
|
13
|
-
|
|
14
|
-
instances = await fleet.instances(status="running")
|
|
15
|
-
print("Instances:", len(instances))
|
|
16
|
-
|
|
17
|
-
instance = await fleet.instance("16fdbc96")
|
|
18
|
-
print("Instance:", instance.instance_id)
|
|
19
|
-
print("Instance Environment:", instance.env_key)
|
|
20
|
-
|
|
21
|
-
environment = await fleet.environment(instance.env_key)
|
|
22
|
-
print("Environment Default Version:", environment.default_version)
|
|
23
|
-
|
|
24
|
-
response = await instance.env.reset()
|
|
25
|
-
print("Reset response:", response)
|
|
26
|
-
|
|
27
|
-
print(await instance.env.resources())
|
|
28
|
-
|
|
29
|
-
sqlite = instance.env.sqlite("current")
|
|
30
|
-
print("SQLite:", await sqlite.describe())
|
|
31
|
-
|
|
32
|
-
print("Query:", await sqlite.query("SELECT * FROM users"))
|
|
33
|
-
|
|
34
|
-
sqlite = await instance.env.state("sqlite://current").describe()
|
|
35
|
-
print("SQLite:", sqlite)
|
|
36
|
-
|
|
37
|
-
browser = await instance.env.browser("cdp").describe()
|
|
38
|
-
print("CDP URL:", browser.url)
|
|
39
|
-
print("CDP Devtools URL:", browser.devtools_url)
|
|
40
|
-
|
|
41
|
-
# Create a new instance
|
|
42
|
-
instance = await fleet.make(flt.InstanceRequest(env_key=instance.env_key))
|
|
43
|
-
print("New Instance:", instance.instance_id)
|
|
44
|
-
|
|
45
|
-
# Delete the instance
|
|
46
|
-
instance = await fleet.delete(instance.instance_id)
|
|
47
|
-
print("Instance deleted:", instance.terminated_at)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if __name__ == "__main__":
|
|
51
|
-
asyncio.run(main())
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
examples/browser_control_example.py,sha256=2nEzUc83bliqR9jYTenkrLTGoKSkWahjP4WpiaHIMxw,1520
|
|
2
|
-
examples/quickstart.py,sha256=AnLlLQYRfrP7y2J69d1HZRlZsjJT-KeBu897MoNfqYM,4671
|
|
3
|
-
fleet/__init__.py,sha256=F8esCp6DcdAc4JAhWlCd_G3pBKRBzuLuVZnElWv5GGQ,987
|
|
4
|
-
fleet/base.py,sha256=lgKhPZhLotP4Iqn7Z4nQTfCvuD3bT37MoucTrSRE2zs,2006
|
|
5
|
-
fleet/client.py,sha256=xMyKc49YwTAh1F5Wbk5hdaB4F6wlrl41a3EjyBdjFUk,5660
|
|
6
|
-
fleet/exceptions.py,sha256=yG3QWprCw1OnF-vdFBFJWE4m3ftBLBng31Dr__VbjI4,2249
|
|
7
|
-
fleet/models.py,sha256=Jf6Zmk689TPXhTSnVENK_VCw0VsujWzEWsN3T29MQ0k,3713
|
|
8
|
-
fleet/env/__init__.py,sha256=ScTHui5BAFTTKKZ6TxS71D5r-JYMxxFEkcT_AcAXDsM,145
|
|
9
|
-
fleet/env/base.py,sha256=bm6BGd81TnTDqE_qG6S50Xhikf9DwNqEEk1uKFY7dEk,1540
|
|
10
|
-
fleet/env/client.py,sha256=yVS4cblGlARWeoZC69Uz_KR2wlmYC19d-BsQalLB9kw,8043
|
|
11
|
-
fleet/env/models.py,sha256=mxRoQEzswMEjwdEN-gn2ylG9Z8IZH-Gqs6hVG0fkSVs,3883
|
|
12
|
-
fleet/resources/base.py,sha256=rzFXBoFqtkM0HZpwqN9NHiwQbmeKOGOYh7G4REaHKGc,553
|
|
13
|
-
fleet/resources/browser.py,sha256=mC1g7xpyvEK8vDGXNn0MXGc8TGE83PIW3KOOj9i3rIA,591
|
|
14
|
-
fleet/resources/sqlite.py,sha256=hZA2qcd7SqjsACJEkKzXQwlJCtvNZ9yom2HCjKy7lOs,1484
|
|
15
|
-
fleet_python-0.2.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
16
|
-
fleet_python-0.2.0.dist-info/METADATA,sha256=qrpy0oFm5wfUvW7VVqbrdZX566B-CB1iJSp4spH9gmg,3069
|
|
17
|
-
fleet_python-0.2.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
18
|
-
fleet_python-0.2.0.dist-info/top_level.txt,sha256=AOyXOrBXUjPcH4BumElz_D95kiWKNIpUbUPFP_9gCLk,15
|
|
19
|
-
fleet_python-0.2.0.dist-info/RECORD,,
|
/fleet/{env → manager}/base.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|