meshagent-cli 0.7.0__py3-none-any.whl → 0.21.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.
- meshagent/cli/agent.py +15 -11
- meshagent/cli/api_keys.py +4 -4
- meshagent/cli/async_typer.py +52 -4
- meshagent/cli/call.py +12 -8
- meshagent/cli/chatbot.py +1007 -129
- meshagent/cli/cli.py +21 -20
- meshagent/cli/cli_mcp.py +92 -28
- meshagent/cli/cli_secrets.py +10 -10
- meshagent/cli/common_options.py +19 -4
- meshagent/cli/containers.py +164 -16
- meshagent/cli/database.py +997 -0
- meshagent/cli/developer.py +3 -3
- meshagent/cli/exec.py +22 -6
- meshagent/cli/helper.py +62 -11
- meshagent/cli/helpers.py +66 -9
- meshagent/cli/host.py +37 -0
- meshagent/cli/mailbot.py +1004 -40
- meshagent/cli/mailboxes.py +223 -0
- meshagent/cli/meeting_transcriber.py +10 -4
- meshagent/cli/messaging.py +7 -7
- meshagent/cli/multi.py +402 -0
- meshagent/cli/oauth2.py +44 -21
- meshagent/cli/participant_token.py +5 -3
- meshagent/cli/port.py +70 -0
- meshagent/cli/queue.py +2 -2
- meshagent/cli/room.py +20 -212
- meshagent/cli/rooms.py +214 -0
- meshagent/cli/services.py +32 -23
- meshagent/cli/sessions.py +5 -5
- meshagent/cli/storage.py +5 -5
- meshagent/cli/task_runner.py +770 -0
- meshagent/cli/version.py +1 -1
- meshagent/cli/voicebot.py +502 -76
- meshagent/cli/webhook.py +7 -7
- meshagent/cli/worker.py +1327 -0
- {meshagent_cli-0.7.0.dist-info → meshagent_cli-0.21.0.dist-info}/METADATA +13 -13
- meshagent_cli-0.21.0.dist-info/RECORD +44 -0
- meshagent_cli-0.7.0.dist-info/RECORD +0 -36
- {meshagent_cli-0.7.0.dist-info → meshagent_cli-0.21.0.dist-info}/WHEEL +0 -0
- {meshagent_cli-0.7.0.dist-info → meshagent_cli-0.21.0.dist-info}/entry_points.txt +0 -0
- {meshagent_cli-0.7.0.dist-info → meshagent_cli-0.21.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,997 @@
|
|
|
1
|
+
import json as _json
|
|
2
|
+
from typing import Annotated, Optional, List, Any
|
|
3
|
+
from urllib.parse import urlparse
|
|
4
|
+
from urllib.request import urlopen
|
|
5
|
+
|
|
6
|
+
import typer
|
|
7
|
+
from rich import print
|
|
8
|
+
|
|
9
|
+
from meshagent.api import RequiredTable
|
|
10
|
+
from meshagent.agents.agent import install_required_table
|
|
11
|
+
|
|
12
|
+
from meshagent.cli.common_options import ProjectIdOption, RoomOption
|
|
13
|
+
from meshagent.cli import async_typer
|
|
14
|
+
from meshagent.cli.helper import resolve_project_id, resolve_room, get_client
|
|
15
|
+
from meshagent.api.helpers import meshagent_base_url, websocket_room_url
|
|
16
|
+
from meshagent.api import RoomClient, WebSocketClientProtocol
|
|
17
|
+
from meshagent.api.room_server_client import DataType
|
|
18
|
+
from meshagent.api import RoomException # or wherever you defined it
|
|
19
|
+
|
|
20
|
+
app = async_typer.AsyncTyper(help="Manage database tables in a room")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# ---------------------------
|
|
24
|
+
# Helpers
|
|
25
|
+
# ---------------------------
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _parse_json_arg(json_str: Optional[str], *, name: str) -> Any:
|
|
29
|
+
if json_str is None:
|
|
30
|
+
return None
|
|
31
|
+
try:
|
|
32
|
+
return _json.loads(json_str)
|
|
33
|
+
except Exception as e:
|
|
34
|
+
raise typer.BadParameter(f"Invalid JSON for {name}: {e}")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _load_json_file(path: Optional[str], *, name: str) -> Any:
|
|
38
|
+
"""
|
|
39
|
+
Load JSON from a local file path or an HTTP(S) URL.
|
|
40
|
+
"""
|
|
41
|
+
if path is None:
|
|
42
|
+
return None
|
|
43
|
+
|
|
44
|
+
try:
|
|
45
|
+
parsed = urlparse(path)
|
|
46
|
+
|
|
47
|
+
# URL case
|
|
48
|
+
if parsed.scheme in ("http", "https"):
|
|
49
|
+
with urlopen(path) as resp:
|
|
50
|
+
charset = resp.headers.get_content_charset() or "utf-8"
|
|
51
|
+
data = resp.read().decode(charset)
|
|
52
|
+
return _json.loads(data)
|
|
53
|
+
|
|
54
|
+
# Local file case
|
|
55
|
+
with open(path, "r", encoding="utf-8") as f:
|
|
56
|
+
return _json.load(f)
|
|
57
|
+
|
|
58
|
+
except Exception as e:
|
|
59
|
+
raise typer.BadParameter(f"Unable to read {name} from {path}: {e}")
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _ns(namespace: Optional[List[str]]) -> Optional[List[str]]:
|
|
63
|
+
return namespace or None
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
NamespaceOption = Annotated[
|
|
67
|
+
Optional[List[str]],
|
|
68
|
+
typer.Option(
|
|
69
|
+
"--namespace",
|
|
70
|
+
"-n",
|
|
71
|
+
help="Namespace path segments (repeatable). Example: -n prod -n analytics",
|
|
72
|
+
),
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
# ---------------------------
|
|
77
|
+
# Commands
|
|
78
|
+
# ---------------------------
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@app.async_command("tables")
|
|
82
|
+
async def list_tables(
|
|
83
|
+
*,
|
|
84
|
+
project_id: ProjectIdOption,
|
|
85
|
+
room: RoomOption,
|
|
86
|
+
namespace: NamespaceOption = None,
|
|
87
|
+
):
|
|
88
|
+
account_client = await get_client()
|
|
89
|
+
try:
|
|
90
|
+
project_id = await resolve_project_id(project_id=project_id)
|
|
91
|
+
room_name = resolve_room(room)
|
|
92
|
+
connection = await account_client.connect_room(
|
|
93
|
+
project_id=project_id, room=room_name
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
async with RoomClient(
|
|
97
|
+
protocol=WebSocketClientProtocol(
|
|
98
|
+
url=websocket_room_url(
|
|
99
|
+
room_name=room_name, base_url=meshagent_base_url()
|
|
100
|
+
),
|
|
101
|
+
token=connection.jwt,
|
|
102
|
+
)
|
|
103
|
+
) as client:
|
|
104
|
+
tables = await client.database.list_tables(namespace=_ns(namespace))
|
|
105
|
+
if not tables:
|
|
106
|
+
print("[bold yellow]No tables found.[/bold yellow]")
|
|
107
|
+
else:
|
|
108
|
+
for t in tables:
|
|
109
|
+
print(t)
|
|
110
|
+
|
|
111
|
+
except RoomException as e:
|
|
112
|
+
print(f"[red]{e}[/red]")
|
|
113
|
+
raise typer.Exit(1)
|
|
114
|
+
finally:
|
|
115
|
+
await account_client.close()
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
@app.async_command("inspect")
|
|
119
|
+
async def inspect(
|
|
120
|
+
*,
|
|
121
|
+
project_id: ProjectIdOption,
|
|
122
|
+
room: RoomOption,
|
|
123
|
+
table: Annotated[str, typer.Option(..., "--table", "-t", help="Table name")],
|
|
124
|
+
namespace: NamespaceOption = None,
|
|
125
|
+
json: Annotated[
|
|
126
|
+
bool, typer.Option("--json", help="Output raw schema JSON")
|
|
127
|
+
] = False,
|
|
128
|
+
):
|
|
129
|
+
account_client = await get_client()
|
|
130
|
+
try:
|
|
131
|
+
project_id = await resolve_project_id(project_id=project_id)
|
|
132
|
+
room_name = resolve_room(room)
|
|
133
|
+
connection = await account_client.connect_room(
|
|
134
|
+
project_id=project_id, room=room_name
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
async with RoomClient(
|
|
138
|
+
protocol=WebSocketClientProtocol(
|
|
139
|
+
url=websocket_room_url(
|
|
140
|
+
room_name=room_name, base_url=meshagent_base_url()
|
|
141
|
+
),
|
|
142
|
+
token=connection.jwt,
|
|
143
|
+
)
|
|
144
|
+
) as client:
|
|
145
|
+
schema = await client.database.inspect(
|
|
146
|
+
table=table, namespace=_ns(namespace)
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
if json:
|
|
150
|
+
# schema values are DataType objects; they expose to_json()
|
|
151
|
+
out = {k: v.to_json() for k, v in schema.items()}
|
|
152
|
+
print(_json.dumps(out, indent=2))
|
|
153
|
+
else:
|
|
154
|
+
print(f"[bold]{table}[/bold]")
|
|
155
|
+
for k, v in schema.items():
|
|
156
|
+
print(f" [cyan]{k}[/cyan]: {v.to_json()}")
|
|
157
|
+
|
|
158
|
+
except RoomException as e:
|
|
159
|
+
print(f"[red]{e}[/red]")
|
|
160
|
+
raise typer.Exit(1)
|
|
161
|
+
finally:
|
|
162
|
+
await account_client.close()
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
@app.async_command("install")
|
|
166
|
+
async def install_requirements(
|
|
167
|
+
*,
|
|
168
|
+
project_id: ProjectIdOption,
|
|
169
|
+
room: RoomOption,
|
|
170
|
+
file: Annotated[
|
|
171
|
+
Optional[str], typer.Option("--file", help="Path to requirements JSON file")
|
|
172
|
+
] = None,
|
|
173
|
+
):
|
|
174
|
+
"""
|
|
175
|
+
Create a database from a json file containing a list of RequiredTables.
|
|
176
|
+
"""
|
|
177
|
+
account_client = await get_client()
|
|
178
|
+
try:
|
|
179
|
+
project_id = await resolve_project_id(project_id=project_id)
|
|
180
|
+
room_name = resolve_room(room)
|
|
181
|
+
connection = await account_client.connect_room(
|
|
182
|
+
project_id=project_id, room=room_name
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
requirements = _load_json_file(file, name="--file")
|
|
186
|
+
|
|
187
|
+
async with RoomClient(
|
|
188
|
+
protocol=WebSocketClientProtocol(
|
|
189
|
+
url=websocket_room_url(
|
|
190
|
+
room_name=room_name, base_url=meshagent_base_url()
|
|
191
|
+
),
|
|
192
|
+
token=connection.jwt,
|
|
193
|
+
)
|
|
194
|
+
) as client:
|
|
195
|
+
for rt in requirements["tables"]:
|
|
196
|
+
rt = RequiredTable.from_json(rt)
|
|
197
|
+
print(f"installing table {rt.name} in namespace {rt.namespace}")
|
|
198
|
+
await install_required_table(room=client, table=rt)
|
|
199
|
+
|
|
200
|
+
except RoomException as e:
|
|
201
|
+
print(f"[red]{e}[/red]")
|
|
202
|
+
raise typer.Exit(1)
|
|
203
|
+
finally:
|
|
204
|
+
await account_client.close()
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
@app.async_command("create")
|
|
208
|
+
async def create_table(
|
|
209
|
+
*,
|
|
210
|
+
project_id: ProjectIdOption,
|
|
211
|
+
room: RoomOption,
|
|
212
|
+
table: Annotated[str, typer.Option(..., "--table", "-t", help="Table name")],
|
|
213
|
+
mode: Annotated[
|
|
214
|
+
str, typer.Option("--mode", help="create | overwrite | create_if_not_exists")
|
|
215
|
+
] = "create",
|
|
216
|
+
namespace: NamespaceOption = None,
|
|
217
|
+
schema_json: Annotated[
|
|
218
|
+
Optional[str], typer.Option("--schema-json", help="Schema JSON as a string")
|
|
219
|
+
] = None,
|
|
220
|
+
schema_file: Annotated[
|
|
221
|
+
Optional[str], typer.Option("--schema-file", help="Path to schema JSON file")
|
|
222
|
+
] = None,
|
|
223
|
+
data_json: Annotated[
|
|
224
|
+
Optional[str], typer.Option("--data-json", help="Initial rows (JSON list)")
|
|
225
|
+
] = None,
|
|
226
|
+
data_file: Annotated[
|
|
227
|
+
Optional[str],
|
|
228
|
+
typer.Option("--data-file", help="Path to JSON file with initial rows"),
|
|
229
|
+
] = None,
|
|
230
|
+
):
|
|
231
|
+
"""
|
|
232
|
+
Create a table with optional schema + optional initial data.
|
|
233
|
+
|
|
234
|
+
Schema JSON format matches your DataType.to_json() structure, e.g.:
|
|
235
|
+
{"id":{"type":"int"}, "body":{"type":"text"}, "embedding":{"type":"vector","size":1536,"element_type":{"type":"float"}}}
|
|
236
|
+
"""
|
|
237
|
+
account_client = await get_client()
|
|
238
|
+
try:
|
|
239
|
+
project_id = await resolve_project_id(project_id=project_id)
|
|
240
|
+
room_name = resolve_room(room)
|
|
241
|
+
connection = await account_client.connect_room(
|
|
242
|
+
project_id=project_id, room=room_name
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
schema_obj = _parse_json_arg(schema_json, name="--schema-json")
|
|
246
|
+
schema_obj = (
|
|
247
|
+
schema_obj
|
|
248
|
+
if schema_obj is not None
|
|
249
|
+
else _load_json_file(schema_file, name="--schema-file")
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
data_obj = _parse_json_arg(data_json, name="--data-json")
|
|
253
|
+
data_obj = (
|
|
254
|
+
data_obj
|
|
255
|
+
if data_obj is not None
|
|
256
|
+
else _load_json_file(data_file, name="--data-file")
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
async with RoomClient(
|
|
260
|
+
protocol=WebSocketClientProtocol(
|
|
261
|
+
url=websocket_room_url(
|
|
262
|
+
room_name=room_name, base_url=meshagent_base_url()
|
|
263
|
+
),
|
|
264
|
+
token=connection.jwt,
|
|
265
|
+
)
|
|
266
|
+
) as client:
|
|
267
|
+
# Build DataType objects from json if schema provided
|
|
268
|
+
schema = None
|
|
269
|
+
if schema_obj is not None:
|
|
270
|
+
schema = {
|
|
271
|
+
k: DataType.from_json(v) for k, v in schema_obj.items()
|
|
272
|
+
} # hacky but local import-safe
|
|
273
|
+
|
|
274
|
+
if schema is not None:
|
|
275
|
+
await client.database.create_table_with_schema(
|
|
276
|
+
name=table,
|
|
277
|
+
schema=schema,
|
|
278
|
+
data=data_obj,
|
|
279
|
+
mode=mode, # type: ignore
|
|
280
|
+
namespace=_ns(namespace),
|
|
281
|
+
)
|
|
282
|
+
else:
|
|
283
|
+
await client.database.create_table_from_data(
|
|
284
|
+
name=table,
|
|
285
|
+
data=data_obj,
|
|
286
|
+
mode=mode, # type: ignore
|
|
287
|
+
namespace=_ns(namespace),
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
print(f"[bold green]Created table:[/bold green] {table}")
|
|
291
|
+
|
|
292
|
+
except RoomException as e:
|
|
293
|
+
print(f"[red]{e}[/red]")
|
|
294
|
+
raise typer.Exit(1)
|
|
295
|
+
finally:
|
|
296
|
+
await account_client.close()
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
@app.async_command("drop")
|
|
300
|
+
async def drop_table(
|
|
301
|
+
*,
|
|
302
|
+
project_id: ProjectIdOption,
|
|
303
|
+
room: RoomOption,
|
|
304
|
+
table: Annotated[str, typer.Option(..., "--table", "-t", help="Table name")],
|
|
305
|
+
namespace: NamespaceOption = None,
|
|
306
|
+
ignore_missing: Annotated[
|
|
307
|
+
bool, typer.Option("--ignore-missing", help="Ignore missing table")
|
|
308
|
+
] = False,
|
|
309
|
+
):
|
|
310
|
+
account_client = await get_client()
|
|
311
|
+
try:
|
|
312
|
+
project_id = await resolve_project_id(project_id=project_id)
|
|
313
|
+
room_name = resolve_room(room)
|
|
314
|
+
connection = await account_client.connect_room(
|
|
315
|
+
project_id=project_id, room=room_name
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
async with RoomClient(
|
|
319
|
+
protocol=WebSocketClientProtocol(
|
|
320
|
+
url=websocket_room_url(
|
|
321
|
+
room_name=room_name, base_url=meshagent_base_url()
|
|
322
|
+
),
|
|
323
|
+
token=connection.jwt,
|
|
324
|
+
)
|
|
325
|
+
) as client:
|
|
326
|
+
await client.database.drop_table(
|
|
327
|
+
name=table, ignore_missing=ignore_missing, namespace=_ns(namespace)
|
|
328
|
+
)
|
|
329
|
+
print(f"[bold green]Dropped table:[/bold green] {table}")
|
|
330
|
+
|
|
331
|
+
except RoomException as e:
|
|
332
|
+
print(f"[red]{e}[/red]")
|
|
333
|
+
raise typer.Exit(1)
|
|
334
|
+
finally:
|
|
335
|
+
await account_client.close()
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
@app.async_command("add-columns")
|
|
339
|
+
async def add_columns(
|
|
340
|
+
*,
|
|
341
|
+
project_id: ProjectIdOption,
|
|
342
|
+
room: RoomOption,
|
|
343
|
+
table: Annotated[str, typer.Option(..., "--table", "-t", help="Table name")],
|
|
344
|
+
namespace: NamespaceOption = None,
|
|
345
|
+
columns_json: Annotated[
|
|
346
|
+
str, typer.Option(..., "--columns-json", help="JSON object of new columns")
|
|
347
|
+
] = None,
|
|
348
|
+
):
|
|
349
|
+
"""
|
|
350
|
+
Add columns. JSON supports either:
|
|
351
|
+
- DataType JSON: {"col":{"type":"text"}}
|
|
352
|
+
- or server default SQL expr strings: {"col":"'default'"}
|
|
353
|
+
"""
|
|
354
|
+
account_client = await get_client()
|
|
355
|
+
try:
|
|
356
|
+
cols_obj = _parse_json_arg(columns_json, name="--columns-json")
|
|
357
|
+
if not isinstance(cols_obj, dict):
|
|
358
|
+
raise typer.BadParameter("--columns-json must be a JSON object")
|
|
359
|
+
|
|
360
|
+
project_id = await resolve_project_id(project_id=project_id)
|
|
361
|
+
room_name = resolve_room(room)
|
|
362
|
+
connection = await account_client.connect_room(
|
|
363
|
+
project_id=project_id, room=room_name
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
async with RoomClient(
|
|
367
|
+
protocol=WebSocketClientProtocol(
|
|
368
|
+
url=websocket_room_url(
|
|
369
|
+
room_name=room_name, base_url=meshagent_base_url()
|
|
370
|
+
),
|
|
371
|
+
token=connection.jwt,
|
|
372
|
+
)
|
|
373
|
+
) as client:
|
|
374
|
+
# Convert DataType json objects into DataType instances; pass strings through.
|
|
375
|
+
new_cols = {}
|
|
376
|
+
for k, v in cols_obj.items():
|
|
377
|
+
if isinstance(v, dict) and "type" in v:
|
|
378
|
+
new_cols[k] = DataType.from_json(v)
|
|
379
|
+
else:
|
|
380
|
+
new_cols[k] = v
|
|
381
|
+
|
|
382
|
+
await client.database.add_columns(
|
|
383
|
+
table=table, new_columns=new_cols, namespace=_ns(namespace)
|
|
384
|
+
)
|
|
385
|
+
print(f"[bold green]Added columns to[/bold green] {table}")
|
|
386
|
+
|
|
387
|
+
except (RoomException, typer.BadParameter) as e:
|
|
388
|
+
print(f"[red]{e}[/red]")
|
|
389
|
+
raise typer.Exit(1)
|
|
390
|
+
finally:
|
|
391
|
+
await account_client.close()
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
@app.async_command("drop-columns")
|
|
395
|
+
async def drop_columns(
|
|
396
|
+
*,
|
|
397
|
+
project_id: ProjectIdOption,
|
|
398
|
+
room: RoomOption,
|
|
399
|
+
table: Annotated[str, typer.Option(..., "--table", "-t", help="Table name")],
|
|
400
|
+
namespace: NamespaceOption = None,
|
|
401
|
+
columns: Annotated[
|
|
402
|
+
List[str],
|
|
403
|
+
typer.Option(..., "--column", "-c", help="Column to drop (repeatable)"),
|
|
404
|
+
] = None,
|
|
405
|
+
):
|
|
406
|
+
account_client = await get_client()
|
|
407
|
+
try:
|
|
408
|
+
project_id = await resolve_project_id(project_id=project_id)
|
|
409
|
+
room_name = resolve_room(room)
|
|
410
|
+
connection = await account_client.connect_room(
|
|
411
|
+
project_id=project_id, room=room_name
|
|
412
|
+
)
|
|
413
|
+
|
|
414
|
+
async with RoomClient(
|
|
415
|
+
protocol=WebSocketClientProtocol(
|
|
416
|
+
url=websocket_room_url(
|
|
417
|
+
room_name=room_name, base_url=meshagent_base_url()
|
|
418
|
+
),
|
|
419
|
+
token=connection.jwt,
|
|
420
|
+
)
|
|
421
|
+
) as client:
|
|
422
|
+
await client.database.drop_columns(
|
|
423
|
+
table=table, columns=columns, namespace=_ns(namespace)
|
|
424
|
+
)
|
|
425
|
+
print(f"[bold green]Dropped columns from[/bold green] {table}")
|
|
426
|
+
|
|
427
|
+
except RoomException as e:
|
|
428
|
+
print(f"[red]{e}[/red]")
|
|
429
|
+
raise typer.Exit(1)
|
|
430
|
+
finally:
|
|
431
|
+
await account_client.close()
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
@app.async_command("insert")
|
|
435
|
+
async def insert(
|
|
436
|
+
*,
|
|
437
|
+
project_id: ProjectIdOption,
|
|
438
|
+
room: RoomOption,
|
|
439
|
+
table: Annotated[str, typer.Option(..., "--table", "-t", help="Table name")],
|
|
440
|
+
namespace: NamespaceOption = None,
|
|
441
|
+
json: Annotated[
|
|
442
|
+
Optional[str], typer.Option("--json", help="JSON list of records")
|
|
443
|
+
] = None,
|
|
444
|
+
file: Annotated[
|
|
445
|
+
Optional[str],
|
|
446
|
+
typer.Option("--file", "-f", help="Path to JSON file (list of records)"),
|
|
447
|
+
] = None,
|
|
448
|
+
):
|
|
449
|
+
account_client = await get_client()
|
|
450
|
+
try:
|
|
451
|
+
records = _parse_json_arg(json, name="--json")
|
|
452
|
+
records = (
|
|
453
|
+
records if records is not None else _load_json_file(file, name="--file")
|
|
454
|
+
)
|
|
455
|
+
if not isinstance(records, list):
|
|
456
|
+
raise typer.BadParameter("insert expects a JSON list of records")
|
|
457
|
+
|
|
458
|
+
project_id = await resolve_project_id(project_id=project_id)
|
|
459
|
+
room_name = resolve_room(room)
|
|
460
|
+
connection = await account_client.connect_room(
|
|
461
|
+
project_id=project_id, room=room_name
|
|
462
|
+
)
|
|
463
|
+
|
|
464
|
+
async with RoomClient(
|
|
465
|
+
protocol=WebSocketClientProtocol(
|
|
466
|
+
url=websocket_room_url(
|
|
467
|
+
room_name=room_name, base_url=meshagent_base_url()
|
|
468
|
+
),
|
|
469
|
+
token=connection.jwt,
|
|
470
|
+
)
|
|
471
|
+
) as client:
|
|
472
|
+
await client.database.insert(
|
|
473
|
+
table=table, records=records, namespace=_ns(namespace)
|
|
474
|
+
)
|
|
475
|
+
print(
|
|
476
|
+
f"[bold green]Inserted[/bold green] {len(records)} record(s) into {table}"
|
|
477
|
+
)
|
|
478
|
+
|
|
479
|
+
except (RoomException, typer.BadParameter) as e:
|
|
480
|
+
print(f"[red]{e}[/red]")
|
|
481
|
+
raise typer.Exit(1)
|
|
482
|
+
finally:
|
|
483
|
+
await account_client.close()
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
@app.async_command("merge")
|
|
487
|
+
async def merge(
|
|
488
|
+
*,
|
|
489
|
+
project_id: ProjectIdOption,
|
|
490
|
+
room: RoomOption,
|
|
491
|
+
table: Annotated[str, typer.Option(..., "--table", "-t", help="Table name")],
|
|
492
|
+
on: Annotated[str, typer.Option(..., "--on", help="Column to match for upsert")],
|
|
493
|
+
namespace: NamespaceOption = None,
|
|
494
|
+
json: Annotated[
|
|
495
|
+
Optional[str], typer.Option("--json", help="JSON records (list)")
|
|
496
|
+
] = None,
|
|
497
|
+
file: Annotated[
|
|
498
|
+
Optional[str], typer.Option("--file", "-f", help="Path to JSON file (list)")
|
|
499
|
+
] = None,
|
|
500
|
+
):
|
|
501
|
+
account_client = await get_client()
|
|
502
|
+
try:
|
|
503
|
+
records = _parse_json_arg(json, name="--json")
|
|
504
|
+
records = (
|
|
505
|
+
records if records is not None else _load_json_file(file, name="--file")
|
|
506
|
+
)
|
|
507
|
+
if not isinstance(records, list):
|
|
508
|
+
raise typer.BadParameter("merge expects a JSON list of records")
|
|
509
|
+
|
|
510
|
+
project_id = await resolve_project_id(project_id=project_id)
|
|
511
|
+
room_name = resolve_room(room)
|
|
512
|
+
connection = await account_client.connect_room(
|
|
513
|
+
project_id=project_id, room=room_name
|
|
514
|
+
)
|
|
515
|
+
|
|
516
|
+
async with RoomClient(
|
|
517
|
+
protocol=WebSocketClientProtocol(
|
|
518
|
+
url=websocket_room_url(
|
|
519
|
+
room_name=room_name, base_url=meshagent_base_url()
|
|
520
|
+
),
|
|
521
|
+
token=connection.jwt,
|
|
522
|
+
)
|
|
523
|
+
) as client:
|
|
524
|
+
await client.database.merge(
|
|
525
|
+
table=table, on=on, records=records, namespace=_ns(namespace)
|
|
526
|
+
)
|
|
527
|
+
print(
|
|
528
|
+
f"[bold green]Merged[/bold green] {len(records)} record(s) into {table} on {on}"
|
|
529
|
+
)
|
|
530
|
+
|
|
531
|
+
except (RoomException, typer.BadParameter) as e:
|
|
532
|
+
print(f"[red]{e}[/red]")
|
|
533
|
+
raise typer.Exit(1)
|
|
534
|
+
finally:
|
|
535
|
+
await account_client.close()
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
@app.async_command("update")
|
|
539
|
+
async def update(
|
|
540
|
+
*,
|
|
541
|
+
project_id: ProjectIdOption,
|
|
542
|
+
room: RoomOption,
|
|
543
|
+
table: Annotated[str, typer.Option(..., "--table", "-t", help="Table name")],
|
|
544
|
+
where: Annotated[
|
|
545
|
+
str, typer.Option(..., "--where", help='SQL WHERE clause, e.g. "id = 1"')
|
|
546
|
+
],
|
|
547
|
+
namespace: NamespaceOption = None,
|
|
548
|
+
values_json: Annotated[
|
|
549
|
+
Optional[str],
|
|
550
|
+
typer.Option("--values-json", help="JSON object of literal values"),
|
|
551
|
+
] = None,
|
|
552
|
+
values_sql_json: Annotated[
|
|
553
|
+
Optional[str],
|
|
554
|
+
typer.Option("--values-sql-json", help="JSON object of SQL expressions"),
|
|
555
|
+
] = None,
|
|
556
|
+
):
|
|
557
|
+
account_client = await get_client()
|
|
558
|
+
try:
|
|
559
|
+
values = _parse_json_arg(values_json, name="--values-json")
|
|
560
|
+
values_sql = _parse_json_arg(values_sql_json, name="--values-sql-json")
|
|
561
|
+
if values is None and values_sql is None:
|
|
562
|
+
raise typer.BadParameter("Provide --values-json and/or --values-sql-json")
|
|
563
|
+
|
|
564
|
+
project_id = await resolve_project_id(project_id=project_id)
|
|
565
|
+
room_name = resolve_room(room)
|
|
566
|
+
connection = await account_client.connect_room(
|
|
567
|
+
project_id=project_id, room=room_name
|
|
568
|
+
)
|
|
569
|
+
|
|
570
|
+
async with RoomClient(
|
|
571
|
+
protocol=WebSocketClientProtocol(
|
|
572
|
+
url=websocket_room_url(
|
|
573
|
+
room_name=room_name, base_url=meshagent_base_url()
|
|
574
|
+
),
|
|
575
|
+
token=connection.jwt,
|
|
576
|
+
)
|
|
577
|
+
) as client:
|
|
578
|
+
await client.database.update(
|
|
579
|
+
table=table,
|
|
580
|
+
where=where,
|
|
581
|
+
values=values,
|
|
582
|
+
values_sql=values_sql,
|
|
583
|
+
namespace=_ns(namespace),
|
|
584
|
+
)
|
|
585
|
+
print(f"[bold green]Updated[/bold green] {table} where {where}")
|
|
586
|
+
|
|
587
|
+
except (RoomException, typer.BadParameter) as e:
|
|
588
|
+
print(f"[red]{e}[/red]")
|
|
589
|
+
raise typer.Exit(1)
|
|
590
|
+
finally:
|
|
591
|
+
await account_client.close()
|
|
592
|
+
|
|
593
|
+
|
|
594
|
+
@app.async_command("delete")
|
|
595
|
+
async def delete(
|
|
596
|
+
*,
|
|
597
|
+
project_id: ProjectIdOption,
|
|
598
|
+
room: RoomOption,
|
|
599
|
+
table: Annotated[str, typer.Option(..., "--table", "-t", help="Table name")],
|
|
600
|
+
where: Annotated[str, typer.Option(..., "--where", help="SQL WHERE clause")],
|
|
601
|
+
namespace: NamespaceOption = None,
|
|
602
|
+
):
|
|
603
|
+
account_client = await get_client()
|
|
604
|
+
try:
|
|
605
|
+
project_id = await resolve_project_id(project_id=project_id)
|
|
606
|
+
room_name = resolve_room(room)
|
|
607
|
+
connection = await account_client.connect_room(
|
|
608
|
+
project_id=project_id, room=room_name
|
|
609
|
+
)
|
|
610
|
+
|
|
611
|
+
async with RoomClient(
|
|
612
|
+
protocol=WebSocketClientProtocol(
|
|
613
|
+
url=websocket_room_url(
|
|
614
|
+
room_name=room_name, base_url=meshagent_base_url()
|
|
615
|
+
),
|
|
616
|
+
token=connection.jwt,
|
|
617
|
+
)
|
|
618
|
+
) as client:
|
|
619
|
+
await client.database.delete(
|
|
620
|
+
table=table, where=where, namespace=_ns(namespace)
|
|
621
|
+
)
|
|
622
|
+
print(f"[bold green]Deleted[/bold green] from {table} where {where}")
|
|
623
|
+
|
|
624
|
+
except RoomException as e:
|
|
625
|
+
print(f"[red]{e}[/red]")
|
|
626
|
+
raise typer.Exit(1)
|
|
627
|
+
finally:
|
|
628
|
+
await account_client.close()
|
|
629
|
+
|
|
630
|
+
|
|
631
|
+
@app.async_command("search")
|
|
632
|
+
async def search(
|
|
633
|
+
*,
|
|
634
|
+
project_id: ProjectIdOption,
|
|
635
|
+
room: RoomOption,
|
|
636
|
+
table: Annotated[str, typer.Option(..., "--table", "-t", help="Table name")],
|
|
637
|
+
namespace: NamespaceOption = None,
|
|
638
|
+
text: Annotated[
|
|
639
|
+
Optional[str], typer.Option("--text", help="Full-text query")
|
|
640
|
+
] = None,
|
|
641
|
+
vector_json: Annotated[
|
|
642
|
+
Optional[str], typer.Option("--vector-json", help="Vector JSON array")
|
|
643
|
+
] = None,
|
|
644
|
+
where: Annotated[
|
|
645
|
+
Optional[str], typer.Option("--where", help="SQL WHERE clause")
|
|
646
|
+
] = None,
|
|
647
|
+
where_json: Annotated[
|
|
648
|
+
Optional[str],
|
|
649
|
+
typer.Option("--where-json", help="JSON object converted to equality ANDs"),
|
|
650
|
+
] = None,
|
|
651
|
+
select: Annotated[
|
|
652
|
+
Optional[List[str]],
|
|
653
|
+
typer.Option("--select", help="Columns to select (repeatable)"),
|
|
654
|
+
] = None,
|
|
655
|
+
limit: Annotated[
|
|
656
|
+
Optional[int], typer.Option("--limit", help="Max rows to return")
|
|
657
|
+
] = None,
|
|
658
|
+
offset: Annotated[
|
|
659
|
+
Optional[int], typer.Option("--offset", help="Rows to skip")
|
|
660
|
+
] = None,
|
|
661
|
+
pretty: Annotated[
|
|
662
|
+
bool, typer.Option("--pretty/--no-pretty", help="Pretty-print JSON")
|
|
663
|
+
] = True,
|
|
664
|
+
):
|
|
665
|
+
account_client = await get_client()
|
|
666
|
+
try:
|
|
667
|
+
vec = _parse_json_arg(vector_json, name="--vector-json")
|
|
668
|
+
if vec is not None and not isinstance(vec, list):
|
|
669
|
+
raise typer.BadParameter("--vector-json must be a JSON array")
|
|
670
|
+
wj = _parse_json_arg(where_json, name="--where-json")
|
|
671
|
+
if wj is not None and not isinstance(wj, dict):
|
|
672
|
+
raise typer.BadParameter("--where-json must be a JSON object")
|
|
673
|
+
|
|
674
|
+
project_id = await resolve_project_id(project_id=project_id)
|
|
675
|
+
room_name = resolve_room(room)
|
|
676
|
+
connection = await account_client.connect_room(
|
|
677
|
+
project_id=project_id, room=room_name
|
|
678
|
+
)
|
|
679
|
+
|
|
680
|
+
async with RoomClient(
|
|
681
|
+
protocol=WebSocketClientProtocol(
|
|
682
|
+
url=websocket_room_url(
|
|
683
|
+
room_name=room_name, base_url=meshagent_base_url()
|
|
684
|
+
),
|
|
685
|
+
token=connection.jwt,
|
|
686
|
+
)
|
|
687
|
+
) as client:
|
|
688
|
+
results = await client.database.search(
|
|
689
|
+
table=table,
|
|
690
|
+
text=text,
|
|
691
|
+
vector=vec,
|
|
692
|
+
where=(wj if wj is not None else where),
|
|
693
|
+
select=list(select) if select else None,
|
|
694
|
+
limit=limit,
|
|
695
|
+
offset=offset,
|
|
696
|
+
namespace=_ns(namespace),
|
|
697
|
+
)
|
|
698
|
+
print(_json.dumps(results, indent=2 if pretty else None))
|
|
699
|
+
|
|
700
|
+
except (RoomException, typer.BadParameter) as e:
|
|
701
|
+
print(f"[red]{e}[/red]")
|
|
702
|
+
raise typer.Exit(1)
|
|
703
|
+
finally:
|
|
704
|
+
await account_client.close()
|
|
705
|
+
|
|
706
|
+
|
|
707
|
+
@app.async_command("optimize")
|
|
708
|
+
async def optimize(
|
|
709
|
+
*,
|
|
710
|
+
project_id: ProjectIdOption,
|
|
711
|
+
room: RoomOption,
|
|
712
|
+
table: Annotated[str, typer.Option(..., "--table", "-t", help="Table name")],
|
|
713
|
+
namespace: NamespaceOption = None,
|
|
714
|
+
):
|
|
715
|
+
account_client = await get_client()
|
|
716
|
+
try:
|
|
717
|
+
project_id = await resolve_project_id(project_id=project_id)
|
|
718
|
+
room_name = resolve_room(room)
|
|
719
|
+
connection = await account_client.connect_room(
|
|
720
|
+
project_id=project_id, room=room_name
|
|
721
|
+
)
|
|
722
|
+
|
|
723
|
+
async with RoomClient(
|
|
724
|
+
protocol=WebSocketClientProtocol(
|
|
725
|
+
url=websocket_room_url(
|
|
726
|
+
room_name=room_name, base_url=meshagent_base_url()
|
|
727
|
+
),
|
|
728
|
+
token=connection.jwt,
|
|
729
|
+
)
|
|
730
|
+
) as client:
|
|
731
|
+
await client.database.optimize(table=table, namespace=_ns(namespace))
|
|
732
|
+
print(f"[bold green]Optimized[/bold green] {table}")
|
|
733
|
+
|
|
734
|
+
except RoomException as e:
|
|
735
|
+
print(f"[red]{e}[/red]")
|
|
736
|
+
raise typer.Exit(1)
|
|
737
|
+
finally:
|
|
738
|
+
await account_client.close()
|
|
739
|
+
|
|
740
|
+
|
|
741
|
+
@app.async_command("versions")
|
|
742
|
+
async def list_versions(
|
|
743
|
+
*,
|
|
744
|
+
project_id: ProjectIdOption,
|
|
745
|
+
room: RoomOption,
|
|
746
|
+
table: Annotated[str, typer.Option(..., "--table", "-t", help="Table name")],
|
|
747
|
+
namespace: NamespaceOption = None,
|
|
748
|
+
pretty: Annotated[
|
|
749
|
+
bool, typer.Option("--pretty/--no-pretty", help="Pretty-print JSON")
|
|
750
|
+
] = True,
|
|
751
|
+
):
|
|
752
|
+
account_client = await get_client()
|
|
753
|
+
try:
|
|
754
|
+
project_id = await resolve_project_id(project_id=project_id)
|
|
755
|
+
room_name = resolve_room(room)
|
|
756
|
+
connection = await account_client.connect_room(
|
|
757
|
+
project_id=project_id, room=room_name
|
|
758
|
+
)
|
|
759
|
+
|
|
760
|
+
async with RoomClient(
|
|
761
|
+
protocol=WebSocketClientProtocol(
|
|
762
|
+
url=websocket_room_url(
|
|
763
|
+
room_name=room_name, base_url=meshagent_base_url()
|
|
764
|
+
),
|
|
765
|
+
token=connection.jwt,
|
|
766
|
+
)
|
|
767
|
+
) as client:
|
|
768
|
+
versions = await client.database.list_versions(
|
|
769
|
+
table=table, namespace=_ns(namespace)
|
|
770
|
+
)
|
|
771
|
+
out = [v.model_dump(mode="json") for v in versions]
|
|
772
|
+
print(_json.dumps(out, indent=2 if pretty else None))
|
|
773
|
+
|
|
774
|
+
except RoomException as e:
|
|
775
|
+
print(f"[red]{e}[/red]")
|
|
776
|
+
raise typer.Exit(1)
|
|
777
|
+
finally:
|
|
778
|
+
await account_client.close()
|
|
779
|
+
|
|
780
|
+
|
|
781
|
+
@app.async_command("checkout")
|
|
782
|
+
async def checkout(
|
|
783
|
+
*,
|
|
784
|
+
project_id: ProjectIdOption,
|
|
785
|
+
room: RoomOption,
|
|
786
|
+
table: Annotated[str, typer.Option(..., "--table", "-t", help="Table name")],
|
|
787
|
+
version: Annotated[int, typer.Option(..., "--version", "-v", help="Table version")],
|
|
788
|
+
namespace: NamespaceOption = None,
|
|
789
|
+
):
|
|
790
|
+
account_client = await get_client()
|
|
791
|
+
try:
|
|
792
|
+
project_id = await resolve_project_id(project_id=project_id)
|
|
793
|
+
room_name = resolve_room(room)
|
|
794
|
+
connection = await account_client.connect_room(
|
|
795
|
+
project_id=project_id, room=room_name
|
|
796
|
+
)
|
|
797
|
+
|
|
798
|
+
async with RoomClient(
|
|
799
|
+
protocol=WebSocketClientProtocol(
|
|
800
|
+
url=websocket_room_url(
|
|
801
|
+
room_name=room_name, base_url=meshagent_base_url()
|
|
802
|
+
),
|
|
803
|
+
token=connection.jwt,
|
|
804
|
+
)
|
|
805
|
+
) as client:
|
|
806
|
+
await client.database.checkout(
|
|
807
|
+
table=table, version=version, namespace=_ns(namespace)
|
|
808
|
+
)
|
|
809
|
+
print(f"[bold green]Checked out[/bold green] {table} @ version {version}")
|
|
810
|
+
|
|
811
|
+
except RoomException as e:
|
|
812
|
+
print(f"[red]{e}[/red]")
|
|
813
|
+
raise typer.Exit(1)
|
|
814
|
+
finally:
|
|
815
|
+
await account_client.close()
|
|
816
|
+
|
|
817
|
+
|
|
818
|
+
@app.async_command("restore")
|
|
819
|
+
async def restore(
|
|
820
|
+
*,
|
|
821
|
+
project_id: ProjectIdOption,
|
|
822
|
+
room: RoomOption,
|
|
823
|
+
table: Annotated[str, typer.Option(..., "--table", "-t", help="Table name")],
|
|
824
|
+
version: Annotated[int, typer.Option(..., "--version", "-v", help="Table version")],
|
|
825
|
+
namespace: NamespaceOption = None,
|
|
826
|
+
):
|
|
827
|
+
account_client = await get_client()
|
|
828
|
+
try:
|
|
829
|
+
project_id = await resolve_project_id(project_id=project_id)
|
|
830
|
+
room_name = resolve_room(room)
|
|
831
|
+
connection = await account_client.connect_room(
|
|
832
|
+
project_id=project_id, room=room_name
|
|
833
|
+
)
|
|
834
|
+
|
|
835
|
+
async with RoomClient(
|
|
836
|
+
protocol=WebSocketClientProtocol(
|
|
837
|
+
url=websocket_room_url(
|
|
838
|
+
room_name=room_name, base_url=meshagent_base_url()
|
|
839
|
+
),
|
|
840
|
+
token=connection.jwt,
|
|
841
|
+
)
|
|
842
|
+
) as client:
|
|
843
|
+
await client.database.restore(
|
|
844
|
+
table=table, version=version, namespace=_ns(namespace)
|
|
845
|
+
)
|
|
846
|
+
print(f"[bold green]Restored[/bold green] {table} to version {version}")
|
|
847
|
+
|
|
848
|
+
except RoomException as e:
|
|
849
|
+
print(f"[red]{e}[/red]")
|
|
850
|
+
raise typer.Exit(1)
|
|
851
|
+
finally:
|
|
852
|
+
await account_client.close()
|
|
853
|
+
|
|
854
|
+
|
|
855
|
+
@app.async_command("indexes")
|
|
856
|
+
async def list_indexes(
|
|
857
|
+
*,
|
|
858
|
+
project_id: ProjectIdOption,
|
|
859
|
+
room: RoomOption,
|
|
860
|
+
table: Annotated[str, typer.Option(..., "--table", "-t", help="Table name")],
|
|
861
|
+
namespace: NamespaceOption = None,
|
|
862
|
+
pretty: Annotated[
|
|
863
|
+
bool, typer.Option("--pretty/--no-pretty", help="Pretty-print JSON")
|
|
864
|
+
] = True,
|
|
865
|
+
):
|
|
866
|
+
account_client = await get_client()
|
|
867
|
+
try:
|
|
868
|
+
project_id = await resolve_project_id(project_id=project_id)
|
|
869
|
+
room_name = resolve_room(room)
|
|
870
|
+
connection = await account_client.connect_room(
|
|
871
|
+
project_id=project_id, room=room_name
|
|
872
|
+
)
|
|
873
|
+
|
|
874
|
+
async with RoomClient(
|
|
875
|
+
protocol=WebSocketClientProtocol(
|
|
876
|
+
url=websocket_room_url(
|
|
877
|
+
room_name=room_name, base_url=meshagent_base_url()
|
|
878
|
+
),
|
|
879
|
+
token=connection.jwt,
|
|
880
|
+
)
|
|
881
|
+
) as client:
|
|
882
|
+
idxs = await client.database.list_indexes(
|
|
883
|
+
table=table, namespace=_ns(namespace)
|
|
884
|
+
)
|
|
885
|
+
out = [i.model_dump(mode="json") for i in idxs]
|
|
886
|
+
print(_json.dumps(out, indent=2 if pretty else None))
|
|
887
|
+
|
|
888
|
+
except RoomException as e:
|
|
889
|
+
print(f"[red]{e}[/red]")
|
|
890
|
+
raise typer.Exit(1)
|
|
891
|
+
finally:
|
|
892
|
+
await account_client.close()
|
|
893
|
+
|
|
894
|
+
|
|
895
|
+
@app.async_command("index-create")
|
|
896
|
+
async def create_index(
|
|
897
|
+
*,
|
|
898
|
+
project_id: ProjectIdOption,
|
|
899
|
+
room: RoomOption,
|
|
900
|
+
table: Annotated[str, typer.Option(..., "--table", "-t", help="Table name")],
|
|
901
|
+
column: Annotated[str, typer.Option(..., "--column", "-c", help="Column name")],
|
|
902
|
+
kind: Annotated[
|
|
903
|
+
str, typer.Option(..., "--kind", help="vector | scalar | fts")
|
|
904
|
+
] = "scalar",
|
|
905
|
+
replace: Annotated[
|
|
906
|
+
Optional[bool],
|
|
907
|
+
typer.Option(
|
|
908
|
+
"--replace/--no-replace",
|
|
909
|
+
help="Replace existing index if it already exists",
|
|
910
|
+
),
|
|
911
|
+
] = None,
|
|
912
|
+
namespace: NamespaceOption = None,
|
|
913
|
+
):
|
|
914
|
+
account_client = await get_client()
|
|
915
|
+
try:
|
|
916
|
+
project_id = await resolve_project_id(project_id=project_id)
|
|
917
|
+
room_name = resolve_room(room)
|
|
918
|
+
connection = await account_client.connect_room(
|
|
919
|
+
project_id=project_id, room=room_name
|
|
920
|
+
)
|
|
921
|
+
|
|
922
|
+
async with RoomClient(
|
|
923
|
+
protocol=WebSocketClientProtocol(
|
|
924
|
+
url=websocket_room_url(
|
|
925
|
+
room_name=room_name, base_url=meshagent_base_url()
|
|
926
|
+
),
|
|
927
|
+
token=connection.jwt,
|
|
928
|
+
)
|
|
929
|
+
) as client:
|
|
930
|
+
if kind == "vector":
|
|
931
|
+
await client.database.create_vector_index(
|
|
932
|
+
table=table,
|
|
933
|
+
column=column,
|
|
934
|
+
replace=replace,
|
|
935
|
+
namespace=_ns(namespace),
|
|
936
|
+
)
|
|
937
|
+
elif kind == "scalar":
|
|
938
|
+
await client.database.create_scalar_index(
|
|
939
|
+
table=table,
|
|
940
|
+
column=column,
|
|
941
|
+
replace=replace,
|
|
942
|
+
namespace=_ns(namespace),
|
|
943
|
+
)
|
|
944
|
+
elif kind in ("fts", "full_text", "full-text"):
|
|
945
|
+
await client.database.create_full_text_search_index(
|
|
946
|
+
table=table,
|
|
947
|
+
column=column,
|
|
948
|
+
replace=replace,
|
|
949
|
+
namespace=_ns(namespace),
|
|
950
|
+
)
|
|
951
|
+
else:
|
|
952
|
+
raise typer.BadParameter("--kind must be one of: vector, scalar, fts")
|
|
953
|
+
|
|
954
|
+
print(f"[bold green]Created[/bold green] {kind} index on {table}.{column}")
|
|
955
|
+
|
|
956
|
+
except (RoomException, typer.BadParameter) as e:
|
|
957
|
+
print(f"[red]{e}[/red]")
|
|
958
|
+
raise typer.Exit(1)
|
|
959
|
+
finally:
|
|
960
|
+
await account_client.close()
|
|
961
|
+
|
|
962
|
+
|
|
963
|
+
@app.async_command("index-drop")
|
|
964
|
+
async def drop_index(
|
|
965
|
+
*,
|
|
966
|
+
project_id: ProjectIdOption,
|
|
967
|
+
room: RoomOption,
|
|
968
|
+
table: Annotated[str, typer.Option(..., "--table", "-t", help="Table name")],
|
|
969
|
+
name: Annotated[str, typer.Option(..., "--name", help="Index name")],
|
|
970
|
+
namespace: NamespaceOption = None,
|
|
971
|
+
):
|
|
972
|
+
account_client = await get_client()
|
|
973
|
+
try:
|
|
974
|
+
project_id = await resolve_project_id(project_id=project_id)
|
|
975
|
+
room_name = resolve_room(room)
|
|
976
|
+
connection = await account_client.connect_room(
|
|
977
|
+
project_id=project_id, room=room_name
|
|
978
|
+
)
|
|
979
|
+
|
|
980
|
+
async with RoomClient(
|
|
981
|
+
protocol=WebSocketClientProtocol(
|
|
982
|
+
url=websocket_room_url(
|
|
983
|
+
room_name=room_name, base_url=meshagent_base_url()
|
|
984
|
+
),
|
|
985
|
+
token=connection.jwt,
|
|
986
|
+
)
|
|
987
|
+
) as client:
|
|
988
|
+
await client.database.drop_index(
|
|
989
|
+
table=table, name=name, namespace=_ns(namespace)
|
|
990
|
+
)
|
|
991
|
+
print(f"[bold green]Dropped index[/bold green] {name} on {table}")
|
|
992
|
+
|
|
993
|
+
except RoomException as e:
|
|
994
|
+
print(f"[red]{e}[/red]")
|
|
995
|
+
raise typer.Exit(1)
|
|
996
|
+
finally:
|
|
997
|
+
await account_client.close()
|