autotouch-cli 0.2.55__tar.gz → 0.2.59__tar.gz
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.
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/PKG-INFO +8 -5
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/README.md +7 -4
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/autotouch_cli/cli.py +31 -0
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/autotouch_cli/commands/auth.py +98 -1
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/autotouch_cli/commands/jobs.py +6 -0
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/autotouch_cli/commands/rows.py +128 -0
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/autotouch_cli/commands/search.py +13 -4
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/autotouch_cli/commands/sequences.py +123 -17
- autotouch_cli-0.2.59/autotouch_cli/commands/tables.py +281 -0
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/autotouch_cli/commands/tasks.py +98 -0
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/autotouch_cli/commands/webhooks.py +35 -0
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/autotouch_cli/core/http.py +8 -2
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/autotouch_cli/core/run.py +79 -0
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/autotouch_cli/data/CLI_REFERENCE.md +506 -38
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/autotouch_cli/data/cli-manifest.json +6573 -3616
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/autotouch_cli/exceptions.py +2 -0
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/autotouch_cli/parser.py +18 -0
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/autotouch_cli/parser_groups.py +85 -0
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/autotouch_cli/sequence_support.py +36 -2
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/autotouch_cli/templates.py +755 -17
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/autotouch_cli.egg-info/PKG-INFO +8 -5
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/autotouch_shared/provider_registry.py +106 -1
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/autotouch_shared/search_contract.py +9 -1
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/pyproject.toml +1 -1
- autotouch_cli-0.2.55/autotouch_cli/commands/tables.py +0 -105
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/MANIFEST.in +0 -0
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/autotouch_cli/__init__.py +0 -0
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/autotouch_cli/cli_contracts.py +0 -0
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/autotouch_cli/commands/__init__.py +0 -0
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/autotouch_cli/commands/cells.py +0 -0
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/autotouch_cli/commands/columns.py +0 -0
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/autotouch_cli/commands/leads.py +0 -0
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/autotouch_cli/commands/linkedin.py +0 -0
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/autotouch_cli/commands/prompts.py +0 -0
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/autotouch_cli/commands/workspace_secrets.py +0 -0
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/autotouch_cli/core/__init__.py +0 -0
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/autotouch_cli/core/auth.py +0 -0
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/autotouch_cli/core/config.py +0 -0
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/autotouch_cli/core/csv_import.py +0 -0
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/autotouch_cli/core/io.py +0 -0
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/autotouch_cli/core/output.py +0 -0
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/autotouch_cli/core/polling.py +0 -0
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/autotouch_cli/core/validation.py +0 -0
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/autotouch_cli/mongo_status.py +0 -0
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/autotouch_cli.egg-info/SOURCES.txt +0 -0
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/autotouch_cli.egg-info/dependency_links.txt +0 -0
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/autotouch_cli.egg-info/entry_points.txt +0 -0
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/autotouch_cli.egg-info/requires.txt +0 -0
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/autotouch_cli.egg-info/top_level.txt +0 -0
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/autotouch_shared/__init__.py +0 -0
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/autotouch_shared/linkedin_contract.py +0 -0
- {autotouch_cli-0.2.55 → autotouch_cli-0.2.59}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: autotouch-cli
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.59
|
|
4
4
|
Summary: Autotouch Smart Table CLI
|
|
5
5
|
Project-URL: Homepage, https://app.autotouch.ai
|
|
6
6
|
Project-URL: Documentation, https://github.com/nicolonic/autotouch_main/tree/main/docs/research-table/reference
|
|
@@ -40,6 +40,8 @@ For a shipped human-readable reference generated from the installed parser, use
|
|
|
40
40
|
pipx install autotouch-cli
|
|
41
41
|
# or
|
|
42
42
|
pip install autotouch-cli
|
|
43
|
+
# upgrade
|
|
44
|
+
pip install -U autotouch-cli
|
|
43
45
|
```
|
|
44
46
|
|
|
45
47
|
## First Run
|
|
@@ -135,6 +137,7 @@ autotouch rows get --table-id "$TABLE_ID" --row-id "$ROW_ID" --output json
|
|
|
135
137
|
- Poll authoritative state: `autotouch jobs get`
|
|
136
138
|
- Create/activate sequences: `autotouch sequences recipe`, `autotouch sequences create`, `autotouch sequences activate`
|
|
137
139
|
- Query leads: `autotouch leads query`
|
|
140
|
+
- Find a lead by email or phone: `autotouch leads query --search '<email-or-phone>' --limit 10`
|
|
138
141
|
|
|
139
142
|
## More
|
|
140
143
|
|
|
@@ -163,7 +166,7 @@ Prompt variables in authored prompts support nested JSON access:
|
|
|
163
166
|
|
|
164
167
|
## Docs
|
|
165
168
|
|
|
166
|
-
- Full CLI reference: https://github.com/nicolonic/autotouch_main/blob/
|
|
167
|
-
- Agent playbook: https://github.com/nicolonic/autotouch_main/blob/
|
|
168
|
-
- Tables/API reference: https://github.com/nicolonic/autotouch_main/blob/
|
|
169
|
-
- Authentication/scopes: https://github.com/nicolonic/autotouch_main/blob/
|
|
169
|
+
- Full CLI reference: https://github.com/nicolonic/autotouch_main/blob/main/docs/research-table/reference/autotouch-cli.md
|
|
170
|
+
- Agent playbook: https://github.com/nicolonic/autotouch_main/blob/main/docs/research-table/guides/autotouch-cli-agent-playbook.md
|
|
171
|
+
- Tables/API reference: https://github.com/nicolonic/autotouch_main/blob/main/docs/research-table/reference/tables-api.md
|
|
172
|
+
- Authentication/scopes: https://github.com/nicolonic/autotouch_main/blob/main/docs/platform/authentication.md
|
|
@@ -15,6 +15,8 @@ For a shipped human-readable reference generated from the installed parser, use
|
|
|
15
15
|
pipx install autotouch-cli
|
|
16
16
|
# or
|
|
17
17
|
pip install autotouch-cli
|
|
18
|
+
# upgrade
|
|
19
|
+
pip install -U autotouch-cli
|
|
18
20
|
```
|
|
19
21
|
|
|
20
22
|
## First Run
|
|
@@ -110,6 +112,7 @@ autotouch rows get --table-id "$TABLE_ID" --row-id "$ROW_ID" --output json
|
|
|
110
112
|
- Poll authoritative state: `autotouch jobs get`
|
|
111
113
|
- Create/activate sequences: `autotouch sequences recipe`, `autotouch sequences create`, `autotouch sequences activate`
|
|
112
114
|
- Query leads: `autotouch leads query`
|
|
115
|
+
- Find a lead by email or phone: `autotouch leads query --search '<email-or-phone>' --limit 10`
|
|
113
116
|
|
|
114
117
|
## More
|
|
115
118
|
|
|
@@ -138,7 +141,7 @@ Prompt variables in authored prompts support nested JSON access:
|
|
|
138
141
|
|
|
139
142
|
## Docs
|
|
140
143
|
|
|
141
|
-
- Full CLI reference: https://github.com/nicolonic/autotouch_main/blob/
|
|
142
|
-
- Agent playbook: https://github.com/nicolonic/autotouch_main/blob/
|
|
143
|
-
- Tables/API reference: https://github.com/nicolonic/autotouch_main/blob/
|
|
144
|
-
- Authentication/scopes: https://github.com/nicolonic/autotouch_main/blob/
|
|
144
|
+
- Full CLI reference: https://github.com/nicolonic/autotouch_main/blob/main/docs/research-table/reference/autotouch-cli.md
|
|
145
|
+
- Agent playbook: https://github.com/nicolonic/autotouch_main/blob/main/docs/research-table/guides/autotouch-cli-agent-playbook.md
|
|
146
|
+
- Tables/API reference: https://github.com/nicolonic/autotouch_main/blob/main/docs/research-table/reference/tables-api.md
|
|
147
|
+
- Authentication/scopes: https://github.com/nicolonic/autotouch_main/blob/main/docs/platform/authentication.md
|
|
@@ -64,6 +64,7 @@ from autotouch_cli.commands.auth import (
|
|
|
64
64
|
cmd_auth_check as cmd_auth_check_impl,
|
|
65
65
|
cmd_auth_clear as cmd_auth_clear_impl,
|
|
66
66
|
cmd_auth_login as cmd_auth_login_impl,
|
|
67
|
+
cmd_auth_status as cmd_auth_status_impl,
|
|
67
68
|
cmd_auth_set_key as cmd_auth_set_key_impl,
|
|
68
69
|
cmd_auth_show as cmd_auth_show_impl,
|
|
69
70
|
cmd_auth_whoami as cmd_auth_whoami_impl,
|
|
@@ -104,6 +105,7 @@ from autotouch_cli.commands.linkedin import (
|
|
|
104
105
|
from autotouch_cli.commands.rows import (
|
|
105
106
|
RowCommandRuntime as RowCommandHandlerRuntime,
|
|
106
107
|
cmd_rows_add as cmd_rows_add_impl,
|
|
108
|
+
cmd_rows_dedupe as cmd_rows_dedupe_impl,
|
|
107
109
|
cmd_rows_delete as cmd_rows_delete_impl,
|
|
108
110
|
cmd_rows_get as cmd_rows_get_impl,
|
|
109
111
|
cmd_rows_import_csv as cmd_rows_import_csv_impl,
|
|
@@ -141,6 +143,10 @@ from autotouch_cli.commands.tables import (
|
|
|
141
143
|
TableCommandRuntime as TableCommandHandlerRuntime,
|
|
142
144
|
cmd_tables_create as cmd_tables_create_impl,
|
|
143
145
|
cmd_tables_list as cmd_tables_list_impl,
|
|
146
|
+
cmd_tables_update as cmd_tables_update_impl,
|
|
147
|
+
cmd_tables_identity_get as cmd_tables_identity_get_impl,
|
|
148
|
+
cmd_tables_identity_set as cmd_tables_identity_set_impl,
|
|
149
|
+
cmd_tables_identity_clear as cmd_tables_identity_clear_impl,
|
|
144
150
|
)
|
|
145
151
|
from autotouch_cli.commands.tasks import (
|
|
146
152
|
TaskCommandRuntime as TaskCommandHandlerRuntime,
|
|
@@ -152,14 +158,20 @@ from autotouch_cli.commands.tasks import (
|
|
|
152
158
|
cmd_tasks_delete as cmd_tasks_delete_impl,
|
|
153
159
|
cmd_tasks_draft as cmd_tasks_draft_impl,
|
|
154
160
|
cmd_tasks_get as cmd_tasks_get_impl,
|
|
161
|
+
cmd_tasks_prioritize_email as cmd_tasks_prioritize_email_impl,
|
|
162
|
+
cmd_tasks_prioritize_linkedin as cmd_tasks_prioritize_linkedin_impl,
|
|
155
163
|
cmd_tasks_query as cmd_tasks_query_impl,
|
|
156
164
|
cmd_tasks_recipe as cmd_tasks_recipe_impl,
|
|
165
|
+
cmd_tasks_reschedule_email as cmd_tasks_reschedule_email_impl,
|
|
166
|
+
cmd_tasks_reschedule_linkedin as cmd_tasks_reschedule_linkedin_impl,
|
|
157
167
|
cmd_tasks_schedule_email as cmd_tasks_schedule_email_impl,
|
|
168
|
+
cmd_tasks_schedule_linkedin as cmd_tasks_schedule_linkedin_impl,
|
|
158
169
|
cmd_tasks_stats as cmd_tasks_stats_impl,
|
|
159
170
|
cmd_tasks_update as cmd_tasks_update_impl,
|
|
160
171
|
)
|
|
161
172
|
from autotouch_cli.commands.webhooks import (
|
|
162
173
|
WebhookCommandRuntime as WebhookCommandHandlerRuntime,
|
|
174
|
+
cmd_webhooks_configure_ingest as cmd_webhooks_configure_ingest_impl,
|
|
163
175
|
cmd_webhooks_deliveries_attempts as cmd_webhooks_deliveries_attempts_impl,
|
|
164
176
|
cmd_webhooks_deliveries_list as cmd_webhooks_deliveries_list_impl,
|
|
165
177
|
cmd_webhooks_get as cmd_webhooks_get_impl,
|
|
@@ -316,6 +328,7 @@ from autotouch_cli.templates import (
|
|
|
316
328
|
emit_webhooks_recipe,
|
|
317
329
|
emit_workflows_list,
|
|
318
330
|
emit_workflows_recipe,
|
|
331
|
+
emit_workflows_scaffold,
|
|
319
332
|
)
|
|
320
333
|
from autotouch_shared.provider_registry import (
|
|
321
334
|
ProviderContractValidationError,
|
|
@@ -1021,6 +1034,9 @@ def _job_snapshot(job_doc: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
1021
1034
|
"processed_rows": int(job_doc.get("processed_rows") or 0),
|
|
1022
1035
|
"total_rows": int(job_doc.get("total_rows") or 0),
|
|
1023
1036
|
"error_rows": int(job_doc.get("error_rows") or 0),
|
|
1037
|
+
"skipped_rows": int(job_doc.get("skipped_rows") or 0),
|
|
1038
|
+
"pending_batches": int(job_doc.get("pending_batches") or 0),
|
|
1039
|
+
"terminal_reason": job_doc.get("terminal_reason"),
|
|
1024
1040
|
"credits_used": float(job_doc.get("credits_used") or 0.0),
|
|
1025
1041
|
"billable": bool(job_doc.get("billable", False)),
|
|
1026
1042
|
"updated_at": job_doc.get("updated_at"),
|
|
@@ -1347,6 +1363,7 @@ def _bound(name: str) -> Callable:
|
|
|
1347
1363
|
|
|
1348
1364
|
# -- Auth commands --
|
|
1349
1365
|
_register("auth_check", cmd_auth_check_impl, _auth_command_runtime)
|
|
1366
|
+
_register("auth_status", cmd_auth_status_impl, _auth_command_runtime)
|
|
1350
1367
|
_register("auth_set_key", cmd_auth_set_key_impl, _auth_command_runtime)
|
|
1351
1368
|
_register("auth_bootstrap", cmd_auth_bootstrap_impl, _auth_command_runtime)
|
|
1352
1369
|
_register("auth_login", cmd_auth_login_impl, _auth_command_runtime)
|
|
@@ -1369,6 +1386,10 @@ _register("workspace_secrets_delete", cmd_workspace_secrets_delete_impl, _worksp
|
|
|
1369
1386
|
# -- Table commands --
|
|
1370
1387
|
_register("tables_list", cmd_tables_list_impl, _table_command_runtime)
|
|
1371
1388
|
_register("tables_create", cmd_tables_create_impl, _table_command_runtime)
|
|
1389
|
+
_register("tables_update", cmd_tables_update_impl, _table_command_runtime)
|
|
1390
|
+
_register("tables_identity_get", cmd_tables_identity_get_impl, _table_command_runtime)
|
|
1391
|
+
_register("tables_identity_set", cmd_tables_identity_set_impl, _table_command_runtime)
|
|
1392
|
+
_register("tables_identity_clear", cmd_tables_identity_clear_impl, _table_command_runtime)
|
|
1372
1393
|
|
|
1373
1394
|
# -- Row commands --
|
|
1374
1395
|
_register("rows_list", cmd_rows_list_impl, _row_command_runtime)
|
|
@@ -1379,6 +1400,7 @@ _register("rows_import_csv", cmd_rows_import_csv_impl, _row_command_runtime)
|
|
|
1379
1400
|
_register("rows_import_status", cmd_rows_import_status_impl, _row_command_runtime)
|
|
1380
1401
|
_register("rows_import_verify", cmd_rows_import_verify_impl, _row_command_runtime)
|
|
1381
1402
|
_register("rows_import_rollback", cmd_rows_import_rollback_impl, _row_command_runtime)
|
|
1403
|
+
_register("rows_dedupe", cmd_rows_dedupe_impl, _row_command_runtime)
|
|
1382
1404
|
|
|
1383
1405
|
# -- Cell commands --
|
|
1384
1406
|
_register("cells_get", cmd_cells_get_impl, _cell_command_runtime)
|
|
@@ -1443,6 +1465,11 @@ _register("tasks_bulk_update", cmd_tasks_bulk_update_impl, _task_command_runtime
|
|
|
1443
1465
|
_register("tasks_bulk_delete", cmd_tasks_bulk_delete_impl, _task_command_runtime)
|
|
1444
1466
|
_register("tasks_draft", cmd_tasks_draft_impl, _task_command_runtime)
|
|
1445
1467
|
_register("tasks_schedule_email", cmd_tasks_schedule_email_impl, _task_command_runtime)
|
|
1468
|
+
_register("tasks_prioritize_email", cmd_tasks_prioritize_email_impl, _task_command_runtime)
|
|
1469
|
+
_register("tasks_reschedule_email", cmd_tasks_reschedule_email_impl, _task_command_runtime)
|
|
1470
|
+
_register("tasks_schedule_linkedin", cmd_tasks_schedule_linkedin_impl, _task_command_runtime)
|
|
1471
|
+
_register("tasks_prioritize_linkedin", cmd_tasks_prioritize_linkedin_impl, _task_command_runtime)
|
|
1472
|
+
_register("tasks_reschedule_linkedin", cmd_tasks_reschedule_linkedin_impl, _task_command_runtime)
|
|
1446
1473
|
_register("tasks_recipe", cmd_tasks_recipe_impl, _task_command_runtime)
|
|
1447
1474
|
|
|
1448
1475
|
# -- Webhook commands --
|
|
@@ -1450,6 +1477,7 @@ _register("webhooks_get", cmd_webhooks_get_impl, _webhook_command_runtime)
|
|
|
1450
1477
|
_register("webhooks_rotate", cmd_webhooks_rotate_impl, _webhook_command_runtime)
|
|
1451
1478
|
_register("webhooks_ingest", cmd_webhooks_ingest_impl, _webhook_command_runtime)
|
|
1452
1479
|
_register("webhooks_recipe", cmd_webhooks_recipe_impl, _webhook_command_runtime)
|
|
1480
|
+
_register("webhooks_configure_ingest", cmd_webhooks_configure_ingest_impl, _webhook_command_runtime)
|
|
1453
1481
|
_register("webhooks_subscriptions_list", cmd_webhooks_subscriptions_list_impl, _webhook_command_runtime)
|
|
1454
1482
|
_register("webhooks_subscriptions_create", cmd_webhooks_subscriptions_create_impl, _webhook_command_runtime)
|
|
1455
1483
|
_register("webhooks_subscriptions_get", cmd_webhooks_subscriptions_get_impl, _webhook_command_runtime)
|
|
@@ -1496,6 +1524,7 @@ _register("sequences_recipe", emit_sequences_recipe, _template_runtime)
|
|
|
1496
1524
|
_register("leads_recipe", emit_leads_recipe, _template_runtime)
|
|
1497
1525
|
_register("workflows_list", emit_workflows_list, _template_runtime)
|
|
1498
1526
|
_register("workflows_recipe", emit_workflows_recipe, _template_runtime)
|
|
1527
|
+
_register("workflows_scaffold", emit_workflows_scaffold, _template_runtime)
|
|
1499
1528
|
|
|
1500
1529
|
# -- Special-case commands that pass extra kwargs --
|
|
1501
1530
|
|
|
@@ -2265,6 +2294,7 @@ def _load_bundled_cli_reference() -> Optional[str]:
|
|
|
2265
2294
|
# ---------------------------------------------------------------------------
|
|
2266
2295
|
cmd_auth_bootstrap = _bound("auth_bootstrap")
|
|
2267
2296
|
cmd_auth_check = _bound("auth_check")
|
|
2297
|
+
cmd_auth_status = _bound("auth_status")
|
|
2268
2298
|
cmd_auth_schema = _bound("auth_schema")
|
|
2269
2299
|
cmd_auth_whoami = _bound("auth_whoami")
|
|
2270
2300
|
cmd_cells_get = _bound("cells_get")
|
|
@@ -2280,6 +2310,7 @@ cmd_schema = _bound("schema")
|
|
|
2280
2310
|
cmd_sequences_enroll = _bound("sequences_enroll")
|
|
2281
2311
|
cmd_sequences_status = _bound("sequences_status")
|
|
2282
2312
|
cmd_tasks_recipe = _bound("tasks_recipe")
|
|
2313
|
+
cmd_workflows_scaffold = _bound("workflows_scaffold")
|
|
2283
2314
|
cmd_webhooks_recipe = _bound("webhooks_recipe")
|
|
2284
2315
|
cmd_workspace_secrets_delete = _bound("workspace_secrets_delete")
|
|
2285
2316
|
cmd_workspace_secrets_schema = _bound("workspace_secrets_schema")
|
|
@@ -45,6 +45,86 @@ def cmd_auth_check(args: argparse.Namespace, *, runtime: AuthCommandRuntime) ->
|
|
|
45
45
|
runtime.print_json(output, args.compact)
|
|
46
46
|
|
|
47
47
|
|
|
48
|
+
def cmd_auth_status(args: argparse.Namespace, *, runtime: AuthCommandRuntime) -> None:
|
|
49
|
+
token = runtime.resolve_token(args.token, required=True)
|
|
50
|
+
check_body = runtime.request_api(
|
|
51
|
+
"GET",
|
|
52
|
+
"/api/auth/check",
|
|
53
|
+
base_url=args.base_url,
|
|
54
|
+
token=token,
|
|
55
|
+
use_x_api_key=args.use_x_api_key,
|
|
56
|
+
timeout=args.timeout,
|
|
57
|
+
verbose=args.verbose,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
details_body: Any = None
|
|
61
|
+
details_warning: Optional[str] = None
|
|
62
|
+
try:
|
|
63
|
+
details_body = runtime.request_api(
|
|
64
|
+
"GET",
|
|
65
|
+
"/api/auth/user-details",
|
|
66
|
+
base_url=args.base_url,
|
|
67
|
+
token=token,
|
|
68
|
+
use_x_api_key=args.use_x_api_key,
|
|
69
|
+
timeout=args.timeout,
|
|
70
|
+
verbose=args.verbose,
|
|
71
|
+
)
|
|
72
|
+
except SystemExit as exc:
|
|
73
|
+
details_warning = f"user-details lookup failed with exit code {exc.code}"
|
|
74
|
+
except Exception as exc:
|
|
75
|
+
details_warning = str(exc)
|
|
76
|
+
|
|
77
|
+
output: Dict[str, Any] = {
|
|
78
|
+
"ok": bool((check_body or {}).get("ok", True)) if isinstance(check_body, dict) else True,
|
|
79
|
+
"base_url": runtime.api_url(args.base_url),
|
|
80
|
+
"auth_header_mode": "x-api-key" if args.use_x_api_key else "authorization",
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if isinstance(check_body, dict):
|
|
84
|
+
for key in (
|
|
85
|
+
"auth_type",
|
|
86
|
+
"user_id",
|
|
87
|
+
"email",
|
|
88
|
+
"organization_id",
|
|
89
|
+
"api_key_id",
|
|
90
|
+
"api_key_name",
|
|
91
|
+
"granted_scopes",
|
|
92
|
+
):
|
|
93
|
+
if key in check_body:
|
|
94
|
+
output[key] = check_body.get(key)
|
|
95
|
+
|
|
96
|
+
if isinstance(details_body, dict):
|
|
97
|
+
for key in (
|
|
98
|
+
"first_name",
|
|
99
|
+
"last_name",
|
|
100
|
+
"title",
|
|
101
|
+
"organization_name",
|
|
102
|
+
"organization_domain",
|
|
103
|
+
"organization_credits",
|
|
104
|
+
"organization_credits_monthly_quota",
|
|
105
|
+
"organization_next_renewal_date",
|
|
106
|
+
"user_allowance",
|
|
107
|
+
"user_monthly_quota",
|
|
108
|
+
"credit_access",
|
|
109
|
+
"permissions",
|
|
110
|
+
"plan",
|
|
111
|
+
"confirmed",
|
|
112
|
+
):
|
|
113
|
+
if key in details_body:
|
|
114
|
+
output[key] = details_body.get(key)
|
|
115
|
+
if output.get("user_id") is None and details_body.get("id") is not None:
|
|
116
|
+
output["user_id"] = details_body.get("id")
|
|
117
|
+
if output.get("email") is None and details_body.get("email") is not None:
|
|
118
|
+
output["email"] = details_body.get("email")
|
|
119
|
+
if output.get("organization_id") is None and details_body.get("organization_id") is not None:
|
|
120
|
+
output["organization_id"] = details_body.get("organization_id")
|
|
121
|
+
|
|
122
|
+
if details_warning:
|
|
123
|
+
output["details_warning"] = details_warning
|
|
124
|
+
|
|
125
|
+
runtime.print_json(output, args.compact)
|
|
126
|
+
|
|
127
|
+
|
|
48
128
|
def cmd_auth_set_key(args: argparse.Namespace, *, runtime: AuthCommandRuntime) -> None:
|
|
49
129
|
api_key = (args.api_key or "").strip() if getattr(args, "api_key", None) else None
|
|
50
130
|
if not api_key:
|
|
@@ -357,6 +437,18 @@ def register_auth_subcommands(
|
|
|
357
437
|
add_api_common_arguments(pa)
|
|
358
438
|
pa.set_defaults(func=handlers["check"])
|
|
359
439
|
|
|
440
|
+
pastatus = auth_sub.add_parser(
|
|
441
|
+
"status",
|
|
442
|
+
help="Resolve auth, identity, and credit/allowance context",
|
|
443
|
+
description=(
|
|
444
|
+
"Fetch the effective auth context plus organization/user credit state. "
|
|
445
|
+
"This is the best one-command status check when a key is valid but pointed at the wrong org "
|
|
446
|
+
"or the real blocker is allowance/quota state rather than authentication."
|
|
447
|
+
),
|
|
448
|
+
)
|
|
449
|
+
add_api_common_arguments(pastatus)
|
|
450
|
+
pastatus.set_defaults(func=handlers["status"])
|
|
451
|
+
|
|
360
452
|
pab = auth_sub.add_parser("bootstrap", help="Register an account/org and return a developer key")
|
|
361
453
|
pab.add_argument("--first-name", help="First name")
|
|
362
454
|
pab.add_argument("--last-name", help="Last name")
|
|
@@ -396,7 +488,12 @@ def register_auth_subcommands(
|
|
|
396
488
|
add_output_formatting_arguments(pask)
|
|
397
489
|
pask.set_defaults(func=handlers["set_key"])
|
|
398
490
|
|
|
399
|
-
pashow = auth_sub.add_parser(
|
|
491
|
+
pashow = auth_sub.add_parser(
|
|
492
|
+
"show",
|
|
493
|
+
aliases=["config"],
|
|
494
|
+
help="Show current local auth config",
|
|
495
|
+
description="Show the locally saved auth config only. This command does not contact the API.",
|
|
496
|
+
)
|
|
400
497
|
add_output_formatting_arguments(pashow)
|
|
401
498
|
pashow.set_defaults(func=handlers["show"])
|
|
402
499
|
|
|
@@ -82,6 +82,12 @@ def cmd_jobs_watch(args: argparse.Namespace, *, runtime: JobCommandRuntime) -> N
|
|
|
82
82
|
"event": "job.timed_out" if timed_out else "job.completed",
|
|
83
83
|
"job_id": final_job.get("job_id") or final_job.get("jobId") or args.job_id,
|
|
84
84
|
"status": final_job.get("status"),
|
|
85
|
+
"processed_rows": int(final_job.get("processed_rows") or 0),
|
|
86
|
+
"error_rows": int(final_job.get("error_rows") or 0),
|
|
87
|
+
"skipped_rows": int(final_job.get("skipped_rows") or 0),
|
|
88
|
+
"total_rows": int(final_job.get("total_rows") or 0),
|
|
89
|
+
"pending_batches": int(final_job.get("pending_batches") or 0),
|
|
90
|
+
"terminal_reason": final_job.get("terminal_reason"),
|
|
85
91
|
"job_status_url": f"{runtime.api_url(args.base_url)}/api/bulk-jobs/{args.job_id}",
|
|
86
92
|
"timed_out": timed_out,
|
|
87
93
|
"polls": int(poll_result.get("polls") or 0),
|
|
@@ -35,6 +35,23 @@ class RowCommandRuntime:
|
|
|
35
35
|
default_timeout_seconds: int
|
|
36
36
|
|
|
37
37
|
|
|
38
|
+
def _parse_json_cell_values(value: Any) -> Any:
|
|
39
|
+
if isinstance(value, str):
|
|
40
|
+
stripped = value.strip()
|
|
41
|
+
if stripped[:1] in {"{", "["}:
|
|
42
|
+
try:
|
|
43
|
+
parsed = json.loads(stripped)
|
|
44
|
+
except Exception:
|
|
45
|
+
return value
|
|
46
|
+
return _parse_json_cell_values(parsed)
|
|
47
|
+
return value
|
|
48
|
+
if isinstance(value, list):
|
|
49
|
+
return [_parse_json_cell_values(item) for item in value]
|
|
50
|
+
if isinstance(value, dict):
|
|
51
|
+
return {key: _parse_json_cell_values(item) for key, item in value.items()}
|
|
52
|
+
return value
|
|
53
|
+
|
|
54
|
+
|
|
38
55
|
def cmd_rows_list(args: argparse.Namespace, *, runtime: RowCommandRuntime) -> None:
|
|
39
56
|
token = runtime.resolve_token(args.token, required=True)
|
|
40
57
|
filters = runtime.load_json_input(
|
|
@@ -73,6 +90,8 @@ def cmd_rows_list(args: argparse.Namespace, *, runtime: RowCommandRuntime) -> No
|
|
|
73
90
|
timeout=args.timeout,
|
|
74
91
|
verbose=args.verbose,
|
|
75
92
|
)
|
|
93
|
+
if bool(getattr(args, "parse_json_cells", False)):
|
|
94
|
+
data = _parse_json_cell_values(data)
|
|
76
95
|
runtime.print_json(data, args.compact)
|
|
77
96
|
|
|
78
97
|
|
|
@@ -87,6 +106,8 @@ def cmd_rows_get(args: argparse.Namespace, *, runtime: RowCommandRuntime) -> Non
|
|
|
87
106
|
timeout=args.timeout,
|
|
88
107
|
verbose=args.verbose,
|
|
89
108
|
)
|
|
109
|
+
if bool(getattr(args, "parse_json_cells", False)):
|
|
110
|
+
data = _parse_json_cell_values(data)
|
|
90
111
|
runtime.print_json(data, args.compact)
|
|
91
112
|
|
|
92
113
|
|
|
@@ -489,6 +510,23 @@ def cmd_rows_import_csv(args: argparse.Namespace, *, runtime: RowCommandRuntime)
|
|
|
489
510
|
if field_mappings is not None:
|
|
490
511
|
form_fields["field_mappings"] = json.dumps(field_mappings)
|
|
491
512
|
|
|
513
|
+
ingest_mode = str(getattr(args, "ingest_mode", None) or "").strip()
|
|
514
|
+
if ingest_mode:
|
|
515
|
+
ingest_policy: Dict[str, Any] = {"mode": ingest_mode}
|
|
516
|
+
match_rule_id = str(getattr(args, "match_rule_id", None) or "").strip()
|
|
517
|
+
match_column = str(getattr(args, "match_column", None) or "").strip()
|
|
518
|
+
match_strategy = str(getattr(args, "match_strategy", None) or "").strip()
|
|
519
|
+
merge_mode = str(getattr(args, "merge_mode", None) or "").strip()
|
|
520
|
+
if match_rule_id:
|
|
521
|
+
ingest_policy["rule_id"] = match_rule_id
|
|
522
|
+
if match_column:
|
|
523
|
+
ingest_policy["column_key"] = match_column
|
|
524
|
+
if match_strategy:
|
|
525
|
+
ingest_policy["match_strategy"] = match_strategy
|
|
526
|
+
if merge_mode:
|
|
527
|
+
ingest_policy["merge_mode"] = merge_mode
|
|
528
|
+
form_fields["ingest_policy"] = json.dumps(ingest_policy)
|
|
529
|
+
|
|
492
530
|
use_async = bool(getattr(args, "use_async", True))
|
|
493
531
|
params = {"use_async": "true" if use_async else "false"}
|
|
494
532
|
response = runtime.request_multipart_api(
|
|
@@ -718,6 +756,50 @@ def cmd_rows_import_rollback(args: argparse.Namespace, *, runtime: RowCommandRun
|
|
|
718
756
|
raise AutotouchAPIError(f"import rollback had {failed_count} failures")
|
|
719
757
|
|
|
720
758
|
|
|
759
|
+
def cmd_rows_dedupe(args: argparse.Namespace, *, runtime: RowCommandRuntime) -> None:
|
|
760
|
+
token = runtime.resolve_token(args.token, required=True)
|
|
761
|
+
stats_only = bool(getattr(args, "stats_only", False))
|
|
762
|
+
output_format = str(getattr(args, "output", "json") or "json").strip().lower()
|
|
763
|
+
|
|
764
|
+
if stats_only:
|
|
765
|
+
data = runtime.request_api(
|
|
766
|
+
"GET",
|
|
767
|
+
f"/api/tables/{args.table_id}/dedupe/{args.column_key}/stats",
|
|
768
|
+
base_url=args.base_url,
|
|
769
|
+
token=token,
|
|
770
|
+
use_x_api_key=args.use_x_api_key,
|
|
771
|
+
timeout=args.timeout,
|
|
772
|
+
verbose=args.verbose,
|
|
773
|
+
)
|
|
774
|
+
if output_format == "json" or not isinstance(data, dict):
|
|
775
|
+
runtime.print_json(data, args.compact)
|
|
776
|
+
return
|
|
777
|
+
total_rows = data.get("total_rows", "?")
|
|
778
|
+
unique_values = data.get("unique_values", "?")
|
|
779
|
+
rows_to_delete = data.get("rows_to_delete", "?")
|
|
780
|
+
print(f"Column: {args.column_key}")
|
|
781
|
+
print(f"Total rows: {total_rows}")
|
|
782
|
+
print(f"Unique values: {unique_values}")
|
|
783
|
+
print(f"Rows to delete: {rows_to_delete}")
|
|
784
|
+
print(f"(Run again without --stats-only / --dry-run to delete duplicates)")
|
|
785
|
+
else:
|
|
786
|
+
data = runtime.request_api(
|
|
787
|
+
"POST",
|
|
788
|
+
f"/api/tables/{args.table_id}/dedupe",
|
|
789
|
+
base_url=args.base_url,
|
|
790
|
+
token=token,
|
|
791
|
+
use_x_api_key=args.use_x_api_key,
|
|
792
|
+
payload={"column_key": args.column_key},
|
|
793
|
+
timeout=args.timeout,
|
|
794
|
+
verbose=args.verbose,
|
|
795
|
+
)
|
|
796
|
+
if output_format == "json" or not isinstance(data, dict):
|
|
797
|
+
runtime.print_json(data, args.compact)
|
|
798
|
+
return
|
|
799
|
+
rows_deleted = data.get("rows_deleted", data.get("deleted", "?"))
|
|
800
|
+
print(f"Dedupe complete: {rows_deleted} duplicate rows deleted from column '{args.column_key}'")
|
|
801
|
+
|
|
802
|
+
|
|
721
803
|
def register_rows_subcommands(
|
|
722
804
|
subparsers: argparse._SubParsersAction[argparse.ArgumentParser],
|
|
723
805
|
*,
|
|
@@ -737,12 +819,22 @@ def register_rows_subcommands(
|
|
|
737
819
|
prl.add_argument("--sort-file", help="Optional JSON file for server-side sort")
|
|
738
820
|
prl.add_argument("--page-size", type=int, default=50, help="Rows per page (1-1000, default 50)")
|
|
739
821
|
prl.add_argument("--cursor", help="Pagination cursor from a previous rows list response")
|
|
822
|
+
prl.add_argument(
|
|
823
|
+
"--parse-json-cells",
|
|
824
|
+
action="store_true",
|
|
825
|
+
help="Parse stringified JSON object/array cell values before printing output",
|
|
826
|
+
)
|
|
740
827
|
add_api_common_arguments(prl)
|
|
741
828
|
prl.set_defaults(func=handlers["list"])
|
|
742
829
|
|
|
743
830
|
prg = rows_sub.add_parser("get", help="Get one flattened row by id")
|
|
744
831
|
prg.add_argument("--table-id", required=True)
|
|
745
832
|
prg.add_argument("--row-id", required=True)
|
|
833
|
+
prg.add_argument(
|
|
834
|
+
"--parse-json-cells",
|
|
835
|
+
action="store_true",
|
|
836
|
+
help="Parse stringified JSON object/array cell values before printing output",
|
|
837
|
+
)
|
|
746
838
|
add_api_common_arguments(prg)
|
|
747
839
|
prg.set_defaults(func=handlers["get"])
|
|
748
840
|
|
|
@@ -830,11 +922,47 @@ def register_rows_subcommands(
|
|
|
830
922
|
pric.add_argument("--wait", action="store_true", help="Wait for async optimized import to complete")
|
|
831
923
|
pric.add_argument("--poll-interval", type=int, default=2, help="Polling interval seconds for --wait")
|
|
832
924
|
pric.add_argument("--wait-timeout", type=int, default=0, help="Max seconds to wait (0 = no timeout)")
|
|
925
|
+
_INGEST_MODES = ["allow", "skip", "upsert"]
|
|
926
|
+
_MATCH_STRATEGIES = ["email", "domain", "linkedin_url", "external_id", "exact_text"]
|
|
927
|
+
_MERGE_MODES = ["fill_empty", "overwrite_mapped"]
|
|
928
|
+
pric.add_argument(
|
|
929
|
+
"--ingest-mode",
|
|
930
|
+
choices=_INGEST_MODES,
|
|
931
|
+
default=None,
|
|
932
|
+
help="Duplicate handling: allow / skip / upsert (enables server-side identity resolution)",
|
|
933
|
+
)
|
|
934
|
+
pric.add_argument("--match-rule-id", default=None, help="Use a pre-configured table identity rule by ID")
|
|
935
|
+
pric.add_argument("--match-column", default=None, help="Column key to match on (when no rule-id)")
|
|
936
|
+
pric.add_argument(
|
|
937
|
+
"--match-strategy",
|
|
938
|
+
choices=_MATCH_STRATEGIES,
|
|
939
|
+
default=None,
|
|
940
|
+
help="Normalisation strategy for the match column",
|
|
941
|
+
)
|
|
942
|
+
pric.add_argument(
|
|
943
|
+
"--merge-mode",
|
|
944
|
+
choices=_MERGE_MODES,
|
|
945
|
+
default=None,
|
|
946
|
+
help="Cell merge strategy when upserting (default: fill_empty)",
|
|
947
|
+
)
|
|
833
948
|
pric.set_defaults(trim_headers=True, empty_as_null=True, skip_empty_rows=True)
|
|
834
949
|
pric.set_defaults(check_company_blacklist=True, check_email_blacklist=False, use_async=True)
|
|
835
950
|
add_api_common_arguments(pric)
|
|
836
951
|
pric.set_defaults(func=handlers["import_csv"])
|
|
837
952
|
|
|
953
|
+
prdedupe = rows_sub.add_parser("dedupe", help="Preview or remove duplicate rows by column value")
|
|
954
|
+
prdedupe.add_argument("--table-id", required=True)
|
|
955
|
+
prdedupe.add_argument("--column-key", required=True, help="Column to deduplicate on")
|
|
956
|
+
prdedupe.add_argument(
|
|
957
|
+
"--stats-only",
|
|
958
|
+
"--dry-run",
|
|
959
|
+
dest="stats_only",
|
|
960
|
+
action="store_true",
|
|
961
|
+
help="Print deduplication preview without deleting rows",
|
|
962
|
+
)
|
|
963
|
+
add_api_common_arguments(prdedupe)
|
|
964
|
+
prdedupe.set_defaults(func=handlers["dedupe"])
|
|
965
|
+
|
|
838
966
|
prschema = rows_sub.add_parser("schema", help="Print row payload schemas for add/import helpers")
|
|
839
967
|
prschema.add_argument("--type", choices=["all", *row_schema_types], default="all")
|
|
840
968
|
prschema.add_argument("--out-file", help="Write schema JSON to file")
|
|
@@ -119,13 +119,20 @@ def cmd_search_people(args: argparse.Namespace, *, runtime: SearchCommandRuntime
|
|
|
119
119
|
|
|
120
120
|
def cmd_search_similar_companies(args: argparse.Namespace, *, runtime: SearchCommandRuntime) -> None:
|
|
121
121
|
token = runtime.resolve_token(args.token, required=True)
|
|
122
|
+
payload: Dict[str, Any] = {
|
|
123
|
+
"company_name": str(getattr(args, "company_name", "") or "").strip(),
|
|
124
|
+
"limit": max(1, min(int(getattr(args, "limit", 25) or 25), 100)),
|
|
125
|
+
}
|
|
126
|
+
company_domain = str(getattr(args, "company_domain", "") or "").strip()
|
|
127
|
+
company_description = str(getattr(args, "company_description", "") or "").strip()
|
|
128
|
+
if company_domain:
|
|
129
|
+
payload["company_domain"] = company_domain
|
|
130
|
+
if company_description:
|
|
131
|
+
payload["company_description"] = company_description
|
|
122
132
|
payload = _load_search_payload(
|
|
123
133
|
args,
|
|
124
134
|
runtime=runtime,
|
|
125
|
-
default_payload=
|
|
126
|
-
"company_name": str(getattr(args, "company_name", "") or "").strip(),
|
|
127
|
-
"limit": max(1, min(int(getattr(args, "limit", 25) or 25), 100)),
|
|
128
|
-
},
|
|
135
|
+
default_payload=payload,
|
|
129
136
|
)
|
|
130
137
|
data = runtime.request_api(
|
|
131
138
|
"POST",
|
|
@@ -278,6 +285,8 @@ def register_search_subcommands(
|
|
|
278
285
|
|
|
279
286
|
similar_parser = search_sub.add_parser("similar-companies", help="Find companies similar to a seed company")
|
|
280
287
|
similar_parser.add_argument("--company-name", help="Seed company name")
|
|
288
|
+
similar_parser.add_argument("--company-domain", help="Official company domain to ground similarity search")
|
|
289
|
+
similar_parser.add_argument("--company-description", help="Short semantic company description to improve peer matching")
|
|
281
290
|
similar_parser.add_argument("--limit", type=int, default=25, help="Max results, 1-100 (default: 25)")
|
|
282
291
|
similar_parser.add_argument("--data-json", help="Explicit request payload JSON")
|
|
283
292
|
similar_parser.add_argument("--data-file", help="Path to request payload JSON file")
|