autotouch-cli 0.2.45__tar.gz → 0.2.47__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.45/autotouch_cli.egg-info → autotouch_cli-0.2.47}/PKG-INFO +37 -10
- autotouch_cli-0.2.45/PKG-INFO → autotouch_cli-0.2.47/README.md +20 -18
- {autotouch_cli-0.2.45 → autotouch_cli-0.2.47}/autotouch_cli/cli.py +13 -5
- {autotouch_cli-0.2.45 → autotouch_cli-0.2.47}/autotouch_cli/cli_contracts.py +121 -4
- {autotouch_cli-0.2.45 → autotouch_cli-0.2.47}/autotouch_cli/commands/auth.py +9 -8
- {autotouch_cli-0.2.45 → autotouch_cli-0.2.47}/autotouch_cli/commands/cells.py +5 -1
- {autotouch_cli-0.2.45 → autotouch_cli-0.2.47}/autotouch_cli/commands/columns.py +15 -6
- {autotouch_cli-0.2.45 → autotouch_cli-0.2.47}/autotouch_cli/core/auth.py +4 -3
- autotouch_cli-0.2.47/autotouch_cli/data/CLI_REFERENCE.md +5130 -0
- {autotouch_cli-0.2.45 → autotouch_cli-0.2.47}/autotouch_cli/data/cli-manifest.json +5517 -494
- {autotouch_cli-0.2.45 → autotouch_cli-0.2.47}/autotouch_cli/templates.py +9 -6
- autotouch_cli-0.2.45/README.md → autotouch_cli-0.2.47/autotouch_cli.egg-info/PKG-INFO +45 -9
- {autotouch_cli-0.2.45 → autotouch_cli-0.2.47}/autotouch_cli.egg-info/requires.txt +3 -0
- autotouch_cli-0.2.47/pyproject.toml +46 -0
- autotouch_cli-0.2.45/autotouch_cli/data/CLI_REFERENCE.md +0 -5118
- autotouch_cli-0.2.45/pyproject.toml +0 -25
- {autotouch_cli-0.2.45 → autotouch_cli-0.2.47}/MANIFEST.in +0 -0
- {autotouch_cli-0.2.45 → autotouch_cli-0.2.47}/autotouch_cli/__init__.py +0 -0
- {autotouch_cli-0.2.45 → autotouch_cli-0.2.47}/autotouch_cli/commands/__init__.py +0 -0
- {autotouch_cli-0.2.45 → autotouch_cli-0.2.47}/autotouch_cli/commands/jobs.py +0 -0
- {autotouch_cli-0.2.45 → autotouch_cli-0.2.47}/autotouch_cli/commands/leads.py +0 -0
- {autotouch_cli-0.2.45 → autotouch_cli-0.2.47}/autotouch_cli/commands/linkedin.py +0 -0
- {autotouch_cli-0.2.45 → autotouch_cli-0.2.47}/autotouch_cli/commands/prompts.py +0 -0
- {autotouch_cli-0.2.45 → autotouch_cli-0.2.47}/autotouch_cli/commands/rows.py +0 -0
- {autotouch_cli-0.2.45 → autotouch_cli-0.2.47}/autotouch_cli/commands/search.py +0 -0
- {autotouch_cli-0.2.45 → autotouch_cli-0.2.47}/autotouch_cli/commands/sequences.py +0 -0
- {autotouch_cli-0.2.45 → autotouch_cli-0.2.47}/autotouch_cli/commands/tables.py +0 -0
- {autotouch_cli-0.2.45 → autotouch_cli-0.2.47}/autotouch_cli/commands/tasks.py +0 -0
- {autotouch_cli-0.2.45 → autotouch_cli-0.2.47}/autotouch_cli/commands/webhooks.py +0 -0
- {autotouch_cli-0.2.45 → autotouch_cli-0.2.47}/autotouch_cli/commands/workspace_secrets.py +0 -0
- {autotouch_cli-0.2.45 → autotouch_cli-0.2.47}/autotouch_cli/core/__init__.py +0 -0
- {autotouch_cli-0.2.45 → autotouch_cli-0.2.47}/autotouch_cli/core/config.py +0 -0
- {autotouch_cli-0.2.45 → autotouch_cli-0.2.47}/autotouch_cli/core/http.py +0 -0
- {autotouch_cli-0.2.45 → autotouch_cli-0.2.47}/autotouch_cli/core/io.py +0 -0
- {autotouch_cli-0.2.45 → autotouch_cli-0.2.47}/autotouch_cli/core/output.py +0 -0
- {autotouch_cli-0.2.45 → autotouch_cli-0.2.47}/autotouch_cli/core/polling.py +0 -0
- {autotouch_cli-0.2.45 → autotouch_cli-0.2.47}/autotouch_cli/mongo_status.py +0 -0
- {autotouch_cli-0.2.45 → autotouch_cli-0.2.47}/autotouch_cli/parser_groups.py +0 -0
- {autotouch_cli-0.2.45 → autotouch_cli-0.2.47}/autotouch_cli/sequence_support.py +0 -0
- {autotouch_cli-0.2.45 → autotouch_cli-0.2.47}/autotouch_cli.egg-info/SOURCES.txt +0 -0
- {autotouch_cli-0.2.45 → autotouch_cli-0.2.47}/autotouch_cli.egg-info/dependency_links.txt +0 -0
- {autotouch_cli-0.2.45 → autotouch_cli-0.2.47}/autotouch_cli.egg-info/entry_points.txt +0 -0
- {autotouch_cli-0.2.45 → autotouch_cli-0.2.47}/autotouch_cli.egg-info/top_level.txt +0 -0
- {autotouch_cli-0.2.45 → autotouch_cli-0.2.47}/autotouch_shared/__init__.py +0 -0
- {autotouch_cli-0.2.45 → autotouch_cli-0.2.47}/autotouch_shared/linkedin_contract.py +0 -0
- {autotouch_cli-0.2.45 → autotouch_cli-0.2.47}/autotouch_shared/provider_registry.py +0 -0
- {autotouch_cli-0.2.45 → autotouch_cli-0.2.47}/autotouch_shared/search_contract.py +0 -0
- {autotouch_cli-0.2.45 → autotouch_cli-0.2.47}/setup.cfg +0 -0
|
@@ -1,11 +1,27 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: autotouch-cli
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.47
|
|
4
4
|
Summary: Autotouch Smart Table CLI
|
|
5
|
+
Project-URL: Homepage, https://app.autotouch.ai
|
|
6
|
+
Project-URL: Documentation, https://github.com/nicolonic/autotouch_main/tree/main/docs/research-table/reference
|
|
7
|
+
Project-URL: Source, https://github.com/nicolonic/autotouch_main
|
|
8
|
+
Keywords: autotouch,smart-table,cli,automation,api
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
|
+
Classifier: Topic :: Utilities
|
|
5
19
|
Requires-Python: >=3.9
|
|
6
20
|
Description-Content-Type: text/markdown
|
|
7
21
|
Requires-Dist: requests>=2.31.0
|
|
8
22
|
Requires-Dist: python-dotenv>=1.0.0
|
|
23
|
+
Provides-Extra: mongo
|
|
24
|
+
Requires-Dist: pymongo>=4.7; extra == "mongo"
|
|
9
25
|
|
|
10
26
|
# Autotouch CLI
|
|
11
27
|
|
|
@@ -31,13 +47,15 @@ pip install autotouch-cli
|
|
|
31
47
|
Existing developer key:
|
|
32
48
|
|
|
33
49
|
```bash
|
|
34
|
-
|
|
50
|
+
read -s AUTOTOUCH_API_KEY
|
|
51
|
+
export AUTOTOUCH_API_KEY
|
|
52
|
+
autotouch setup --api-key "$AUTOTOUCH_API_KEY" --base-url https://app.autotouch.ai
|
|
35
53
|
```
|
|
36
54
|
|
|
37
55
|
Equivalent manual steps:
|
|
38
56
|
|
|
39
57
|
```bash
|
|
40
|
-
autotouch auth set-key --api-key
|
|
58
|
+
autotouch auth set-key --api-key "$AUTOTOUCH_API_KEY" --base-url https://app.autotouch.ai
|
|
41
59
|
autotouch auth check --base-url https://app.autotouch.ai
|
|
42
60
|
```
|
|
43
61
|
|
|
@@ -45,23 +63,31 @@ Fresh account:
|
|
|
45
63
|
|
|
46
64
|
```bash
|
|
47
65
|
export AUTOTOUCH_CONFIG_PATH=/tmp/autotouch-audit.json
|
|
66
|
+
read -s AUTOTOUCH_BOOTSTRAP_PASSWORD
|
|
67
|
+
export AUTOTOUCH_BOOTSTRAP_PASSWORD
|
|
48
68
|
|
|
49
69
|
autotouch auth bootstrap \
|
|
50
70
|
--first-name Ada \
|
|
51
71
|
--last-name Lovelace \
|
|
52
72
|
--email ada+audit@example.com \
|
|
53
|
-
--password
|
|
73
|
+
--password "$AUTOTOUCH_BOOTSTRAP_PASSWORD" \
|
|
54
74
|
--organization-name "Audit Org" \
|
|
55
75
|
--save-key
|
|
56
76
|
|
|
57
77
|
autotouch auth check
|
|
58
78
|
```
|
|
59
79
|
|
|
80
|
+
Config precedence:
|
|
81
|
+
- Explicit flags win over environment variables.
|
|
82
|
+
- Environment variables win over saved config.
|
|
83
|
+
- Saved config wins over built-in defaults.
|
|
84
|
+
- For JSON payload inputs, pass either `--data-json` or `--data-file`, not both.
|
|
85
|
+
|
|
60
86
|
## 5-Minute Flow
|
|
61
87
|
|
|
62
88
|
```bash
|
|
63
89
|
# 0) First-run setup
|
|
64
|
-
autotouch setup --api-key
|
|
90
|
+
autotouch setup --api-key "$AUTOTOUCH_API_KEY" --base-url https://app.autotouch.ai
|
|
65
91
|
|
|
66
92
|
# 1) Inspect machine-readable contract
|
|
67
93
|
autotouch capabilities --output json
|
|
@@ -85,7 +111,7 @@ COLUMN_ID=$(autotouch columns create --table-id "$TABLE_ID" --data-file column.j
|
|
|
85
111
|
JOB_ID=$(autotouch columns run-next \
|
|
86
112
|
--table-id "$TABLE_ID" \
|
|
87
113
|
--column-id "$COLUMN_ID" \
|
|
88
|
-
--count
|
|
114
|
+
--count 2 \
|
|
89
115
|
--show-estimate \
|
|
90
116
|
--wait \
|
|
91
117
|
--output json --select job_id)
|
|
@@ -118,10 +144,11 @@ For automation or agent-driven setup, use:
|
|
|
118
144
|
- `autotouch capabilities --output json` for provider/workflow contracts
|
|
119
145
|
- `autotouch rows list` / `autotouch rows get` / `autotouch cells get` for read-back and audit
|
|
120
146
|
- `autotouch sequences ...` and `autotouch tasks ...` for sequence/task workflows
|
|
147
|
+
- `pip install 'autotouch-cli[mongo]'` if you need the Mongo-backed `status` / `watch` commands
|
|
121
148
|
|
|
122
149
|
## Docs
|
|
123
150
|
|
|
124
|
-
- Full CLI reference: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.
|
|
125
|
-
- Agent playbook: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.
|
|
126
|
-
- Tables/API reference: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.
|
|
127
|
-
- Authentication/scopes: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.
|
|
151
|
+
- Full CLI reference: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.47/docs/research-table/reference/autotouch-cli.md
|
|
152
|
+
- Agent playbook: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.47/docs/research-table/guides/autotouch-cli-agent-playbook.md
|
|
153
|
+
- Tables/API reference: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.47/docs/research-table/reference/tables-api.md
|
|
154
|
+
- Authentication/scopes: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.47/docs/platform/authentication.md
|
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: autotouch-cli
|
|
3
|
-
Version: 0.2.45
|
|
4
|
-
Summary: Autotouch Smart Table CLI
|
|
5
|
-
Requires-Python: >=3.9
|
|
6
|
-
Description-Content-Type: text/markdown
|
|
7
|
-
Requires-Dist: requests>=2.31.0
|
|
8
|
-
Requires-Dist: python-dotenv>=1.0.0
|
|
9
|
-
|
|
10
1
|
# Autotouch CLI
|
|
11
2
|
|
|
12
3
|
Installable CLI for the Smart Table developer API.
|
|
@@ -31,13 +22,15 @@ pip install autotouch-cli
|
|
|
31
22
|
Existing developer key:
|
|
32
23
|
|
|
33
24
|
```bash
|
|
34
|
-
|
|
25
|
+
read -s AUTOTOUCH_API_KEY
|
|
26
|
+
export AUTOTOUCH_API_KEY
|
|
27
|
+
autotouch setup --api-key "$AUTOTOUCH_API_KEY" --base-url https://app.autotouch.ai
|
|
35
28
|
```
|
|
36
29
|
|
|
37
30
|
Equivalent manual steps:
|
|
38
31
|
|
|
39
32
|
```bash
|
|
40
|
-
autotouch auth set-key --api-key
|
|
33
|
+
autotouch auth set-key --api-key "$AUTOTOUCH_API_KEY" --base-url https://app.autotouch.ai
|
|
41
34
|
autotouch auth check --base-url https://app.autotouch.ai
|
|
42
35
|
```
|
|
43
36
|
|
|
@@ -45,23 +38,31 @@ Fresh account:
|
|
|
45
38
|
|
|
46
39
|
```bash
|
|
47
40
|
export AUTOTOUCH_CONFIG_PATH=/tmp/autotouch-audit.json
|
|
41
|
+
read -s AUTOTOUCH_BOOTSTRAP_PASSWORD
|
|
42
|
+
export AUTOTOUCH_BOOTSTRAP_PASSWORD
|
|
48
43
|
|
|
49
44
|
autotouch auth bootstrap \
|
|
50
45
|
--first-name Ada \
|
|
51
46
|
--last-name Lovelace \
|
|
52
47
|
--email ada+audit@example.com \
|
|
53
|
-
--password
|
|
48
|
+
--password "$AUTOTOUCH_BOOTSTRAP_PASSWORD" \
|
|
54
49
|
--organization-name "Audit Org" \
|
|
55
50
|
--save-key
|
|
56
51
|
|
|
57
52
|
autotouch auth check
|
|
58
53
|
```
|
|
59
54
|
|
|
55
|
+
Config precedence:
|
|
56
|
+
- Explicit flags win over environment variables.
|
|
57
|
+
- Environment variables win over saved config.
|
|
58
|
+
- Saved config wins over built-in defaults.
|
|
59
|
+
- For JSON payload inputs, pass either `--data-json` or `--data-file`, not both.
|
|
60
|
+
|
|
60
61
|
## 5-Minute Flow
|
|
61
62
|
|
|
62
63
|
```bash
|
|
63
64
|
# 0) First-run setup
|
|
64
|
-
autotouch setup --api-key
|
|
65
|
+
autotouch setup --api-key "$AUTOTOUCH_API_KEY" --base-url https://app.autotouch.ai
|
|
65
66
|
|
|
66
67
|
# 1) Inspect machine-readable contract
|
|
67
68
|
autotouch capabilities --output json
|
|
@@ -85,7 +86,7 @@ COLUMN_ID=$(autotouch columns create --table-id "$TABLE_ID" --data-file column.j
|
|
|
85
86
|
JOB_ID=$(autotouch columns run-next \
|
|
86
87
|
--table-id "$TABLE_ID" \
|
|
87
88
|
--column-id "$COLUMN_ID" \
|
|
88
|
-
--count
|
|
89
|
+
--count 2 \
|
|
89
90
|
--show-estimate \
|
|
90
91
|
--wait \
|
|
91
92
|
--output json --select job_id)
|
|
@@ -118,10 +119,11 @@ For automation or agent-driven setup, use:
|
|
|
118
119
|
- `autotouch capabilities --output json` for provider/workflow contracts
|
|
119
120
|
- `autotouch rows list` / `autotouch rows get` / `autotouch cells get` for read-back and audit
|
|
120
121
|
- `autotouch sequences ...` and `autotouch tasks ...` for sequence/task workflows
|
|
122
|
+
- `pip install 'autotouch-cli[mongo]'` if you need the Mongo-backed `status` / `watch` commands
|
|
121
123
|
|
|
122
124
|
## Docs
|
|
123
125
|
|
|
124
|
-
- Full CLI reference: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.
|
|
125
|
-
- Agent playbook: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.
|
|
126
|
-
- Tables/API reference: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.
|
|
127
|
-
- Authentication/scopes: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.
|
|
126
|
+
- Full CLI reference: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.47/docs/research-table/reference/autotouch-cli.md
|
|
127
|
+
- Agent playbook: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.47/docs/research-table/guides/autotouch-cli-agent-playbook.md
|
|
128
|
+
- Tables/API reference: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.47/docs/research-table/reference/tables-api.md
|
|
129
|
+
- Authentication/scopes: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.47/docs/platform/authentication.md
|
|
@@ -1091,8 +1091,7 @@ def _normalize_run_payload(args: argparse.Namespace) -> Dict[str, Any]:
|
|
|
1091
1091
|
if filters is not None:
|
|
1092
1092
|
payload["filters"] = filters
|
|
1093
1093
|
|
|
1094
|
-
|
|
1095
|
-
payload["unprocessedOnly"] = True
|
|
1094
|
+
payload["unprocessedOnly"] = bool(getattr(args, "unprocessed_only", True))
|
|
1096
1095
|
|
|
1097
1096
|
return payload
|
|
1098
1097
|
|
|
@@ -3559,11 +3558,20 @@ def _add_run_scope_arguments(parser: argparse.ArgumentParser) -> None:
|
|
|
3559
3558
|
type=int,
|
|
3560
3559
|
help="First N rows (for scope=firstN). Use with --unprocessed-only to process the next rows without reruns",
|
|
3561
3560
|
)
|
|
3562
|
-
parser.
|
|
3561
|
+
run_mode = parser.add_mutually_exclusive_group()
|
|
3562
|
+
run_mode.add_argument(
|
|
3563
3563
|
"--unprocessed-only",
|
|
3564
|
+
dest="unprocessed_only",
|
|
3564
3565
|
action="store_true",
|
|
3565
|
-
help="Only process rows without values (
|
|
3566
|
+
help="Only process rows without values (default)",
|
|
3567
|
+
)
|
|
3568
|
+
run_mode.add_argument(
|
|
3569
|
+
"--include-processed",
|
|
3570
|
+
dest="unprocessed_only",
|
|
3571
|
+
action="store_false",
|
|
3572
|
+
help="Include rows that already have output",
|
|
3566
3573
|
)
|
|
3574
|
+
parser.set_defaults(unprocessed_only=True)
|
|
3567
3575
|
parser.add_argument("--filters-json", help="JSON object for scope=filtered")
|
|
3568
3576
|
parser.add_argument("--filters-file", help="Path to JSON file for scope=filtered")
|
|
3569
3577
|
parser.add_argument("--data-json", help="Explicit RunRequest payload JSON (overrides scope flags)")
|
|
@@ -4218,7 +4226,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
4218
4226
|
palias_run_next = sub.add_parser("run-next", help="Alias for: columns run-next")
|
|
4219
4227
|
palias_run_next.add_argument("--table-id", required=True)
|
|
4220
4228
|
palias_run_next.add_argument("--column-id", required=True)
|
|
4221
|
-
palias_run_next.add_argument("--count", type=int, required=True, help="
|
|
4229
|
+
palias_run_next.add_argument("--count", type=int, required=True, help="Maximum number of rows to queue")
|
|
4222
4230
|
palias_run_next.add_argument("--filters-json", help="Optional JSON object to select from filtered rows")
|
|
4223
4231
|
palias_run_next.add_argument("--filters-file", help="Optional JSON file to select from filtered rows")
|
|
4224
4232
|
palias_run_next.add_argument("--page-size", type=int, default=200, help="Rows page size while selecting candidates (max 1000)")
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import argparse
|
|
4
|
+
import json
|
|
4
5
|
from typing import Any, Dict, List, Optional, Tuple
|
|
5
6
|
|
|
6
7
|
|
|
8
|
+
_CLI_MANIFEST_SCHEMA_VERSION = 2
|
|
9
|
+
|
|
10
|
+
|
|
7
11
|
_AUTH_MODE_EXACT: Dict[str, str] = {
|
|
8
12
|
"setup": "none",
|
|
9
13
|
"auth.bootstrap": "none",
|
|
@@ -79,6 +83,80 @@ _OPTIONAL_DEPENDENCY_COMMANDS: Dict[str, Dict[str, Any]] = {
|
|
|
79
83
|
},
|
|
80
84
|
}
|
|
81
85
|
|
|
86
|
+
_SENSITIVE_OPTION_TERMS = ("token", "api-key", "api_key", "password", "secret")
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _json_safe_value(value: Any) -> Any:
|
|
90
|
+
try:
|
|
91
|
+
json.dumps(value)
|
|
92
|
+
except (TypeError, ValueError):
|
|
93
|
+
return str(value)
|
|
94
|
+
return value
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _option_input_kind(action: argparse.Action) -> Optional[str]:
|
|
98
|
+
flags = [flag.lower() for flag in getattr(action, "option_strings", []) or []]
|
|
99
|
+
dest = str(getattr(action, "dest", "") or "").lower()
|
|
100
|
+
if any(flag.endswith("-file") or flag == "--file" for flag in flags) or dest.endswith("_file"):
|
|
101
|
+
return "file"
|
|
102
|
+
if any(flag.endswith("-json") for flag in flags) or dest.endswith("_json"):
|
|
103
|
+
return "json"
|
|
104
|
+
if dest in {"out_file", "checkpoint_file"}:
|
|
105
|
+
return "file"
|
|
106
|
+
return None
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _option_kind(action: argparse.Action, *, input_kind: Optional[str]) -> str:
|
|
110
|
+
if isinstance(action, (argparse._StoreTrueAction, argparse._StoreFalseAction)):
|
|
111
|
+
return "boolean"
|
|
112
|
+
if isinstance(action, argparse._CountAction):
|
|
113
|
+
return "integer"
|
|
114
|
+
if input_kind:
|
|
115
|
+
return input_kind
|
|
116
|
+
if getattr(action, "type", None) is int:
|
|
117
|
+
return "integer"
|
|
118
|
+
if getattr(action, "type", None) is float:
|
|
119
|
+
return "number"
|
|
120
|
+
if getattr(action, "nargs", None) in ("*", "+"):
|
|
121
|
+
return "list"
|
|
122
|
+
return "string"
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _option_action_kind(action: argparse.Action) -> str:
|
|
126
|
+
if isinstance(action, argparse._StoreTrueAction):
|
|
127
|
+
return "store_true"
|
|
128
|
+
if isinstance(action, argparse._StoreFalseAction):
|
|
129
|
+
return "store_false"
|
|
130
|
+
if isinstance(action, argparse._AppendAction):
|
|
131
|
+
return "append"
|
|
132
|
+
if isinstance(action, argparse._AppendConstAction):
|
|
133
|
+
return "append_const"
|
|
134
|
+
if isinstance(action, argparse._CountAction):
|
|
135
|
+
return "count"
|
|
136
|
+
if isinstance(action, argparse._StoreConstAction):
|
|
137
|
+
return "store_const"
|
|
138
|
+
return "store"
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _option_is_sensitive(action: argparse.Action) -> bool:
|
|
142
|
+
option_strings = [flag.lower() for flag in getattr(action, "option_strings", []) or []]
|
|
143
|
+
dest = str(getattr(action, "dest", "") or "").lower()
|
|
144
|
+
if isinstance(action, (argparse._StoreTrueAction, argparse._StoreFalseAction, argparse._StoreConstAction, argparse._AppendConstAction)):
|
|
145
|
+
return False
|
|
146
|
+
if dest == "value" or "--value" in option_strings:
|
|
147
|
+
return True
|
|
148
|
+
searchable = " ".join([dest, *option_strings])
|
|
149
|
+
return any(term in searchable for term in _SENSITIVE_OPTION_TERMS)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _option_accepts_comma_separated(action: argparse.Action) -> bool:
|
|
153
|
+
help_text = str(getattr(action, "help", "") or "").lower()
|
|
154
|
+
return "comma-separated" in help_text
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _option_repeatable(action: argparse.Action) -> bool:
|
|
158
|
+
return isinstance(action, (argparse._AppendAction, argparse._AppendConstAction, argparse._CountAction))
|
|
159
|
+
|
|
82
160
|
|
|
83
161
|
def canonical_subparser_entries(
|
|
84
162
|
action: argparse._SubParsersAction,
|
|
@@ -123,10 +201,14 @@ def action_descriptor(action: argparse.Action) -> Optional[Dict[str, Any]]:
|
|
|
123
201
|
if isinstance(action, argparse._SubParsersAction):
|
|
124
202
|
return None
|
|
125
203
|
|
|
204
|
+
input_kind = _option_input_kind(action)
|
|
205
|
+
action_kind = _option_action_kind(action)
|
|
126
206
|
payload: Dict[str, Any] = {
|
|
127
207
|
"dest": action.dest,
|
|
128
208
|
"required": bool(getattr(action, "required", False)),
|
|
129
209
|
"help": action.help,
|
|
210
|
+
"kind": _option_kind(action, input_kind=input_kind),
|
|
211
|
+
"action": action_kind,
|
|
130
212
|
}
|
|
131
213
|
if getattr(action, "choices", None) is not None:
|
|
132
214
|
payload["choices"] = list(action.choices)
|
|
@@ -135,11 +217,29 @@ def action_descriptor(action: argparse.Action) -> Optional[Dict[str, Any]]:
|
|
|
135
217
|
if getattr(action, "metavar", None) is not None:
|
|
136
218
|
payload["metavar"] = action.metavar
|
|
137
219
|
default_value = getattr(action, "default", argparse.SUPPRESS)
|
|
138
|
-
if default_value is not argparse.SUPPRESS
|
|
139
|
-
|
|
220
|
+
if default_value is not argparse.SUPPRESS:
|
|
221
|
+
if default_value is not None or isinstance(action, (argparse._StoreTrueAction, argparse._StoreFalseAction)):
|
|
222
|
+
payload["default_when_omitted"] = _json_safe_value(default_value)
|
|
223
|
+
if default_value is not None and not isinstance(action, (argparse._StoreTrueAction, argparse._StoreFalseAction)):
|
|
224
|
+
payload["default"] = _json_safe_value(default_value)
|
|
225
|
+
if isinstance(action, argparse._StoreTrueAction):
|
|
226
|
+
payload["value_when_present"] = True
|
|
227
|
+
elif isinstance(action, argparse._StoreFalseAction):
|
|
228
|
+
payload["value_when_present"] = False
|
|
229
|
+
if input_kind:
|
|
230
|
+
payload["input_kind"] = input_kind
|
|
231
|
+
if _option_repeatable(action):
|
|
232
|
+
payload["repeatable"] = True
|
|
233
|
+
if _option_accepts_comma_separated(action):
|
|
234
|
+
payload["accepts_comma_separated"] = True
|
|
235
|
+
if _option_is_sensitive(action):
|
|
236
|
+
payload["sensitive"] = True
|
|
140
237
|
if action.option_strings:
|
|
141
238
|
payload["flags"] = list(action.option_strings)
|
|
142
|
-
payload["takes_value"] =
|
|
239
|
+
payload["takes_value"] = not isinstance(
|
|
240
|
+
action,
|
|
241
|
+
(argparse._StoreTrueAction, argparse._StoreFalseAction, argparse._StoreConstAction, argparse._AppendConstAction),
|
|
242
|
+
) and getattr(action, "nargs", None) != 0
|
|
143
243
|
else:
|
|
144
244
|
payload["name"] = action.dest
|
|
145
245
|
payload["positional"] = True
|
|
@@ -301,6 +401,7 @@ def collect_cli_command_manifest(
|
|
|
301
401
|
def build_cli_manifest(version: str, parser: argparse.ArgumentParser) -> Dict[str, Any]:
|
|
302
402
|
return {
|
|
303
403
|
"version": version,
|
|
404
|
+
"manifest_schema_version": _CLI_MANIFEST_SCHEMA_VERSION,
|
|
304
405
|
"entry_points": {
|
|
305
406
|
"autotouch": "autotouch_cli.cli:main",
|
|
306
407
|
"smart-table": "autotouch_cli.cli:main",
|
|
@@ -311,11 +412,13 @@ def build_cli_manifest(version: str, parser: argparse.ArgumentParser) -> Dict[st
|
|
|
311
412
|
|
|
312
413
|
def build_cli_reference_markdown(manifest: Dict[str, Any]) -> str:
|
|
313
414
|
version = str(manifest.get("version") or "unknown")
|
|
415
|
+
manifest_schema_version = str(manifest.get("manifest_schema_version") or "unknown")
|
|
314
416
|
commands = manifest.get("commands") if isinstance(manifest.get("commands"), dict) else {}
|
|
315
417
|
lines: List[str] = [
|
|
316
418
|
"# Autotouch CLI Reference",
|
|
317
419
|
"",
|
|
318
420
|
f"Generated from the installed parser for `autotouch-cli` `{version}`.",
|
|
421
|
+
f"Manifest schema version: `{manifest_schema_version}`.",
|
|
319
422
|
"",
|
|
320
423
|
"## Output Modes",
|
|
321
424
|
"",
|
|
@@ -394,9 +497,23 @@ def build_cli_reference_markdown(manifest: Dict[str, Any]) -> str:
|
|
|
394
497
|
note_parts: List[str] = []
|
|
395
498
|
if option.get("required"):
|
|
396
499
|
note_parts.append("required")
|
|
500
|
+
if option.get("kind"):
|
|
501
|
+
note_parts.append(f"kind={option.get('kind')}")
|
|
502
|
+
if option.get("input_kind"):
|
|
503
|
+
note_parts.append(f"input={option.get('input_kind')}")
|
|
504
|
+
if option.get("repeatable"):
|
|
505
|
+
note_parts.append("repeatable")
|
|
506
|
+
if option.get("accepts_comma_separated"):
|
|
507
|
+
note_parts.append("comma-separated")
|
|
508
|
+
if option.get("sensitive"):
|
|
509
|
+
note_parts.append("sensitive")
|
|
510
|
+
if option.get("action") in {"store_true", "store_false"}:
|
|
511
|
+
note_parts.append(
|
|
512
|
+
f"when omitted={option.get('default_when_omitted')}; when present={option.get('value_when_present')}"
|
|
513
|
+
)
|
|
397
514
|
if option.get("choices"):
|
|
398
515
|
note_parts.append(f"choices={','.join(str(v) for v in option.get('choices') or [])}")
|
|
399
|
-
if option.get("default") is not None:
|
|
516
|
+
if option.get("default") is not None and option.get("action") not in {"store_true", "store_false"}:
|
|
400
517
|
note_parts.append(f"default={option.get('default')}")
|
|
401
518
|
help_value = str(option.get("help") or "").strip()
|
|
402
519
|
note_text = f" ({'; '.join(note_parts)})" if note_parts else ""
|
|
@@ -30,19 +30,16 @@ def cmd_auth_check(args: argparse.Namespace, *, runtime: AuthCommandRuntime) ->
|
|
|
30
30
|
token = runtime.resolve_token(args.token, required=True)
|
|
31
31
|
body = runtime.request_api(
|
|
32
32
|
"GET",
|
|
33
|
-
"/api/
|
|
33
|
+
"/api/auth/check",
|
|
34
34
|
base_url=args.base_url,
|
|
35
35
|
token=token,
|
|
36
36
|
use_x_api_key=args.use_x_api_key,
|
|
37
37
|
timeout=args.timeout,
|
|
38
38
|
verbose=args.verbose,
|
|
39
39
|
)
|
|
40
|
-
output = {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
"api_version": body.get("api_version") if isinstance(body, dict) else None,
|
|
44
|
-
"auth_header_mode": "x-api-key" if args.use_x_api_key else "authorization",
|
|
45
|
-
}
|
|
40
|
+
output = dict(body) if isinstance(body, dict) else {"ok": True}
|
|
41
|
+
output["base_url"] = runtime.api_url(args.base_url)
|
|
42
|
+
output["auth_header_mode"] = "x-api-key" if args.use_x_api_key else "authorization"
|
|
46
43
|
runtime.print_json(output, args.compact)
|
|
47
44
|
|
|
48
45
|
|
|
@@ -363,7 +360,11 @@ def register_auth_subcommands(
|
|
|
363
360
|
auth = subparsers.add_parser("auth", help="Auth helpers")
|
|
364
361
|
auth_sub = auth.add_subparsers(dest="auth_cmd", required=True)
|
|
365
362
|
|
|
366
|
-
pa = auth_sub.add_parser(
|
|
363
|
+
pa = auth_sub.add_parser(
|
|
364
|
+
"check",
|
|
365
|
+
help="Validate the current token/session and show granted scopes",
|
|
366
|
+
description="Validate the current token/session against the API and show granted scopes for developer API keys.",
|
|
367
|
+
)
|
|
367
368
|
add_api_common_arguments(pa)
|
|
368
369
|
pa.set_defaults(func=handlers["check"])
|
|
369
370
|
|
|
@@ -85,7 +85,11 @@ def register_cells_subcommands(
|
|
|
85
85
|
add_api_common_arguments(get_parser)
|
|
86
86
|
get_parser.set_defaults(func=handlers["get"])
|
|
87
87
|
|
|
88
|
-
patch_parser = cells_sub.add_parser(
|
|
88
|
+
patch_parser = cells_sub.add_parser(
|
|
89
|
+
"patch",
|
|
90
|
+
help="Patch cells in batch",
|
|
91
|
+
description="Patch cells in batch. Use column key strings in updates[].key; this payload does not accept columnId.",
|
|
92
|
+
)
|
|
89
93
|
patch_parser.add_argument("--table-id", required=True)
|
|
90
94
|
patch_parser.add_argument("--updates-json", help="JSON list or {updates:[...]} object")
|
|
91
95
|
patch_parser.add_argument("--updates-file", help="Path to JSON file with updates payload")
|
|
@@ -273,9 +273,11 @@ def cmd_columns_run_next(args: argparse.Namespace, *, runtime: ColumnCommandRunt
|
|
|
273
273
|
sys.exit(3)
|
|
274
274
|
return
|
|
275
275
|
|
|
276
|
-
payload: Dict[str, Any] = {
|
|
277
|
-
|
|
278
|
-
|
|
276
|
+
payload: Dict[str, Any] = {
|
|
277
|
+
"scope": "subset",
|
|
278
|
+
"rowIds": row_ids,
|
|
279
|
+
"unprocessedOnly": bool(args.unprocessed_only),
|
|
280
|
+
}
|
|
279
281
|
|
|
280
282
|
context = {
|
|
281
283
|
"mode": "run-next",
|
|
@@ -327,7 +329,14 @@ def register_columns_subcommands(
|
|
|
327
329
|
add_api_common_arguments(pcc)
|
|
328
330
|
pcc.set_defaults(func=handlers["create"])
|
|
329
331
|
|
|
330
|
-
pcu = col_sub.add_parser(
|
|
332
|
+
pcu = col_sub.add_parser(
|
|
333
|
+
"update",
|
|
334
|
+
help="Update a column",
|
|
335
|
+
description=(
|
|
336
|
+
"Update a column definition. Nested config keys merge into the existing config, "
|
|
337
|
+
"so partial config patches are supported."
|
|
338
|
+
),
|
|
339
|
+
)
|
|
331
340
|
pcu.add_argument("--table-id", required=True)
|
|
332
341
|
pcu.add_argument("--column-id", required=True)
|
|
333
342
|
pcu.add_argument("--data-json", help="ColumnUpdate payload JSON")
|
|
@@ -415,10 +424,10 @@ def register_columns_subcommands(
|
|
|
415
424
|
add_api_common_arguments(pcs)
|
|
416
425
|
pcs.set_defaults(func=handlers["stop"])
|
|
417
426
|
|
|
418
|
-
pcrn = col_sub.add_parser("run-next", help="Run
|
|
427
|
+
pcrn = col_sub.add_parser("run-next", help="Run up to N selected rows using subset scope")
|
|
419
428
|
pcrn.add_argument("--table-id", required=True)
|
|
420
429
|
pcrn.add_argument("--column-id", required=True)
|
|
421
|
-
pcrn.add_argument("--count", type=int, required=True, help="
|
|
430
|
+
pcrn.add_argument("--count", type=int, required=True, help="Maximum number of rows to queue")
|
|
422
431
|
pcrn.add_argument("--filters-json", help="Optional JSON object to select from filtered rows")
|
|
423
432
|
pcrn.add_argument("--filters-file", help="Optional JSON file to select from filtered rows")
|
|
424
433
|
pcrn.add_argument("--page-size", type=int, default=200, help="Rows page size while selecting candidates (max 1000)")
|
|
@@ -68,8 +68,9 @@ def print_missing_developer_auth_help() -> None:
|
|
|
68
68
|
print(
|
|
69
69
|
"Fresh-account / isolated audit pattern: "
|
|
70
70
|
"`export AUTOTOUCH_CONFIG_PATH=/tmp/autotouch-audit.json && "
|
|
71
|
+
"read -s AUTOTOUCH_BOOTSTRAP_PASSWORD && export AUTOTOUCH_BOOTSTRAP_PASSWORD && "
|
|
71
72
|
"autotouch auth bootstrap --first-name Ada --last-name Lovelace "
|
|
72
|
-
"--email ada+audit@example.com --password
|
|
73
|
+
"--email ada+audit@example.com --password \"$AUTOTOUCH_BOOTSTRAP_PASSWORD\" "
|
|
73
74
|
"--organization-name \"Audit Org\" --save-key`",
|
|
74
75
|
file=sys.stderr,
|
|
75
76
|
)
|
|
@@ -89,8 +90,9 @@ def print_missing_user_session_help() -> None:
|
|
|
89
90
|
print(
|
|
90
91
|
"Fresh-account / isolated audit pattern: "
|
|
91
92
|
"`export AUTOTOUCH_CONFIG_PATH=/tmp/autotouch-audit.json && "
|
|
93
|
+
"read -s AUTOTOUCH_BOOTSTRAP_PASSWORD && export AUTOTOUCH_BOOTSTRAP_PASSWORD && "
|
|
92
94
|
"autotouch auth bootstrap --first-name Ada --last-name Lovelace "
|
|
93
|
-
"--email ada+audit@example.com --password
|
|
95
|
+
"--email ada+audit@example.com --password \"$AUTOTOUCH_BOOTSTRAP_PASSWORD\" "
|
|
94
96
|
"--organization-name \"Audit Org\" --save-key`",
|
|
95
97
|
file=sys.stderr,
|
|
96
98
|
)
|
|
@@ -157,4 +159,3 @@ def auth_headers(token: Optional[str], *, use_x_api_key: bool = False) -> Dict[s
|
|
|
157
159
|
if use_x_api_key:
|
|
158
160
|
return {"X-API-Key": token}
|
|
159
161
|
return {"Authorization": f"Bearer {token}"}
|
|
160
|
-
|