autotouch-cli 0.2.40__tar.gz → 0.2.42__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.
Files changed (45) hide show
  1. {autotouch_cli-0.2.40 → autotouch_cli-0.2.42}/PKG-INFO +5 -5
  2. {autotouch_cli-0.2.40 → autotouch_cli-0.2.42}/README.md +4 -4
  3. {autotouch_cli-0.2.40 → autotouch_cli-0.2.42}/autotouch_cli/cli.py +54 -0
  4. {autotouch_cli-0.2.40 → autotouch_cli-0.2.42}/autotouch_cli/cli_contracts.py +17 -0
  5. {autotouch_cli-0.2.40 → autotouch_cli-0.2.42}/autotouch_cli/commands/columns.py +109 -1
  6. autotouch_cli-0.2.42/autotouch_cli/commands/workspace_secrets.py +176 -0
  7. {autotouch_cli-0.2.40 → autotouch_cli-0.2.42}/autotouch_cli/data/CLI_REFERENCE.md +208 -5
  8. {autotouch_cli-0.2.40 → autotouch_cli-0.2.42}/autotouch_cli/data/cli-manifest.json +1145 -4
  9. {autotouch_cli-0.2.40 → autotouch_cli-0.2.42}/autotouch_cli/templates.py +28 -0
  10. {autotouch_cli-0.2.40 → autotouch_cli-0.2.42}/autotouch_cli.egg-info/PKG-INFO +5 -5
  11. {autotouch_cli-0.2.40 → autotouch_cli-0.2.42}/autotouch_cli.egg-info/SOURCES.txt +1 -0
  12. {autotouch_cli-0.2.40 → autotouch_cli-0.2.42}/autotouch_shared/provider_registry.py +122 -0
  13. {autotouch_cli-0.2.40 → autotouch_cli-0.2.42}/pyproject.toml +1 -1
  14. {autotouch_cli-0.2.40 → autotouch_cli-0.2.42}/MANIFEST.in +0 -0
  15. {autotouch_cli-0.2.40 → autotouch_cli-0.2.42}/autotouch_cli/__init__.py +0 -0
  16. {autotouch_cli-0.2.40 → autotouch_cli-0.2.42}/autotouch_cli/commands/__init__.py +0 -0
  17. {autotouch_cli-0.2.40 → autotouch_cli-0.2.42}/autotouch_cli/commands/auth.py +0 -0
  18. {autotouch_cli-0.2.40 → autotouch_cli-0.2.42}/autotouch_cli/commands/cells.py +0 -0
  19. {autotouch_cli-0.2.40 → autotouch_cli-0.2.42}/autotouch_cli/commands/jobs.py +0 -0
  20. {autotouch_cli-0.2.40 → autotouch_cli-0.2.42}/autotouch_cli/commands/leads.py +0 -0
  21. {autotouch_cli-0.2.40 → autotouch_cli-0.2.42}/autotouch_cli/commands/linkedin.py +0 -0
  22. {autotouch_cli-0.2.40 → autotouch_cli-0.2.42}/autotouch_cli/commands/rows.py +0 -0
  23. {autotouch_cli-0.2.40 → autotouch_cli-0.2.42}/autotouch_cli/commands/search.py +0 -0
  24. {autotouch_cli-0.2.40 → autotouch_cli-0.2.42}/autotouch_cli/commands/sequences.py +0 -0
  25. {autotouch_cli-0.2.40 → autotouch_cli-0.2.42}/autotouch_cli/commands/tables.py +0 -0
  26. {autotouch_cli-0.2.40 → autotouch_cli-0.2.42}/autotouch_cli/commands/tasks.py +0 -0
  27. {autotouch_cli-0.2.40 → autotouch_cli-0.2.42}/autotouch_cli/commands/webhooks.py +0 -0
  28. {autotouch_cli-0.2.40 → autotouch_cli-0.2.42}/autotouch_cli/core/__init__.py +0 -0
  29. {autotouch_cli-0.2.40 → autotouch_cli-0.2.42}/autotouch_cli/core/auth.py +0 -0
  30. {autotouch_cli-0.2.40 → autotouch_cli-0.2.42}/autotouch_cli/core/config.py +0 -0
  31. {autotouch_cli-0.2.40 → autotouch_cli-0.2.42}/autotouch_cli/core/http.py +0 -0
  32. {autotouch_cli-0.2.40 → autotouch_cli-0.2.42}/autotouch_cli/core/io.py +0 -0
  33. {autotouch_cli-0.2.40 → autotouch_cli-0.2.42}/autotouch_cli/core/output.py +0 -0
  34. {autotouch_cli-0.2.40 → autotouch_cli-0.2.42}/autotouch_cli/core/polling.py +0 -0
  35. {autotouch_cli-0.2.40 → autotouch_cli-0.2.42}/autotouch_cli/mongo_status.py +0 -0
  36. {autotouch_cli-0.2.40 → autotouch_cli-0.2.42}/autotouch_cli/parser_groups.py +0 -0
  37. {autotouch_cli-0.2.40 → autotouch_cli-0.2.42}/autotouch_cli/sequence_support.py +0 -0
  38. {autotouch_cli-0.2.40 → autotouch_cli-0.2.42}/autotouch_cli.egg-info/dependency_links.txt +0 -0
  39. {autotouch_cli-0.2.40 → autotouch_cli-0.2.42}/autotouch_cli.egg-info/entry_points.txt +0 -0
  40. {autotouch_cli-0.2.40 → autotouch_cli-0.2.42}/autotouch_cli.egg-info/requires.txt +0 -0
  41. {autotouch_cli-0.2.40 → autotouch_cli-0.2.42}/autotouch_cli.egg-info/top_level.txt +0 -0
  42. {autotouch_cli-0.2.40 → autotouch_cli-0.2.42}/autotouch_shared/__init__.py +0 -0
  43. {autotouch_cli-0.2.40 → autotouch_cli-0.2.42}/autotouch_shared/linkedin_contract.py +0 -0
  44. {autotouch_cli-0.2.40 → autotouch_cli-0.2.42}/autotouch_shared/search_contract.py +0 -0
  45. {autotouch_cli-0.2.40 → autotouch_cli-0.2.42}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: autotouch-cli
3
- Version: 0.2.40
3
+ Version: 0.2.42
4
4
  Summary: Autotouch Smart Table CLI
5
5
  Requires-Python: >=3.9
6
6
  Description-Content-Type: text/markdown
@@ -137,7 +137,7 @@ Use a dedicated `AUTOTOUCH_CONFIG_PATH` if you want a clean local account instea
137
137
 
138
138
  ## Docs
139
139
 
140
- - Full CLI reference: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.40/docs/research-table/reference/autotouch-cli.md
141
- - Agent playbook: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.40/docs/research-table/guides/autotouch-cli-agent-playbook.md
142
- - Tables/API reference: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.40/docs/research-table/reference/tables-api.md
143
- - Authentication/scopes: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.40/docs/platform/authentication.md
140
+ - Full CLI reference: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.42/docs/research-table/reference/autotouch-cli.md
141
+ - Agent playbook: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.42/docs/research-table/guides/autotouch-cli-agent-playbook.md
142
+ - Tables/API reference: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.42/docs/research-table/reference/tables-api.md
143
+ - Authentication/scopes: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.42/docs/platform/authentication.md
@@ -128,7 +128,7 @@ Use a dedicated `AUTOTOUCH_CONFIG_PATH` if you want a clean local account instea
128
128
 
129
129
  ## Docs
130
130
 
131
- - Full CLI reference: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.40/docs/research-table/reference/autotouch-cli.md
132
- - Agent playbook: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.40/docs/research-table/guides/autotouch-cli-agent-playbook.md
133
- - Tables/API reference: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.40/docs/research-table/reference/tables-api.md
134
- - Authentication/scopes: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.40/docs/platform/authentication.md
131
+ - Full CLI reference: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.42/docs/research-table/reference/autotouch-cli.md
132
+ - Agent playbook: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.42/docs/research-table/guides/autotouch-cli-agent-playbook.md
133
+ - Tables/API reference: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.42/docs/research-table/reference/tables-api.md
134
+ - Authentication/scopes: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.42/docs/platform/authentication.md
@@ -49,6 +49,7 @@ from autotouch_cli.commands.columns import (
49
49
  cmd_columns_run as cmd_columns_run_impl,
50
50
  cmd_columns_run_next as cmd_columns_run_next_impl,
51
51
  cmd_columns_stop as cmd_columns_stop_impl,
52
+ cmd_columns_test_http_request as cmd_columns_test_http_request_impl,
52
53
  cmd_columns_update as cmd_columns_update_impl,
53
54
  register_columns_subcommands,
54
55
  )
@@ -185,6 +186,13 @@ from autotouch_cli.commands.webhooks import (
185
186
  cmd_webhooks_subscriptions_test as cmd_webhooks_subscriptions_test_impl,
186
187
  cmd_webhooks_subscriptions_update as cmd_webhooks_subscriptions_update_impl,
187
188
  )
189
+ from autotouch_cli.commands.workspace_secrets import (
190
+ WorkspaceSecretCommandRuntime as WorkspaceSecretCommandHandlerRuntime,
191
+ cmd_workspace_secrets_delete as cmd_workspace_secrets_delete_impl,
192
+ cmd_workspace_secrets_list as cmd_workspace_secrets_list_impl,
193
+ cmd_workspace_secrets_set as cmd_workspace_secrets_set_impl,
194
+ register_workspace_secrets_subcommands,
195
+ )
188
196
  from autotouch_cli.core.auth import (
189
197
  auth_headers as auth_headers_impl,
190
198
  env_api_key as env_api_key_impl,
@@ -263,6 +271,7 @@ from autotouch_cli.templates import (
263
271
  ONBOARDING_SCHEMA_TYPES,
264
272
  ORG_CONTEXT_SCHEMA_TYPES,
265
273
  PERSONAL_CONTEXT_SCHEMA_TYPES,
274
+ WORKSPACE_SECRET_SCHEMA_TYPES,
266
275
  ROW_SCHEMA_TYPES,
267
276
  SEQUENCE_RECIPE_TYPES,
268
277
  SHARED_SCHEMA_TYPES,
@@ -279,6 +288,7 @@ from autotouch_cli.templates import (
279
288
  emit_onboarding_schema,
280
289
  emit_org_context_schema,
281
290
  emit_personal_context_schema,
291
+ emit_workspace_secrets_schema,
282
292
  emit_rows_schema,
283
293
  emit_schema,
284
294
  emit_sequences_recipe,
@@ -2252,6 +2262,18 @@ def cmd_auth_clear(args: argparse.Namespace) -> None:
2252
2262
  cmd_auth_clear_impl(args, runtime=_auth_command_runtime())
2253
2263
 
2254
2264
 
2265
+ def cmd_workspace_secrets_list(args: argparse.Namespace) -> None:
2266
+ cmd_workspace_secrets_list_impl(args, runtime=_workspace_secret_command_runtime())
2267
+
2268
+
2269
+ def cmd_workspace_secrets_set(args: argparse.Namespace) -> None:
2270
+ cmd_workspace_secrets_set_impl(args, runtime=_workspace_secret_command_runtime())
2271
+
2272
+
2273
+ def cmd_workspace_secrets_delete(args: argparse.Namespace) -> None:
2274
+ cmd_workspace_secrets_delete_impl(args, runtime=_workspace_secret_command_runtime())
2275
+
2276
+
2255
2277
  def _coerce_string_list(values: Any) -> List[str]:
2256
2278
  if values is None:
2257
2279
  return []
@@ -2626,6 +2648,10 @@ def cmd_personal_context_schema(args: argparse.Namespace) -> None:
2626
2648
  emit_personal_context_schema(args, _template_runtime())
2627
2649
 
2628
2650
 
2651
+ def cmd_workspace_secrets_schema(args: argparse.Namespace) -> None:
2652
+ emit_workspace_secrets_schema(args, _template_runtime())
2653
+
2654
+
2629
2655
  def cmd_tables_schema(args: argparse.Namespace) -> None:
2630
2656
  emit_tables_schema(args, _template_runtime())
2631
2657
 
@@ -2774,6 +2800,16 @@ def _auth_command_runtime() -> AuthCommandHandlerRuntime:
2774
2800
  )
2775
2801
 
2776
2802
 
2803
+ def _workspace_secret_command_runtime() -> WorkspaceSecretCommandHandlerRuntime:
2804
+ return WorkspaceSecretCommandHandlerRuntime(
2805
+ resolve_token=_resolve_token,
2806
+ request_api=_request_api,
2807
+ print_json=lambda data, compact=False: _print_json(data, compact=compact),
2808
+ load_json_input=_load_json_input,
2809
+ normalize_string_value=_normalize_string_value,
2810
+ )
2811
+
2812
+
2777
2813
  def _job_command_runtime() -> JobCommandHandlerRuntime:
2778
2814
  return JobCommandHandlerRuntime(
2779
2815
  resolve_token=_resolve_token,
@@ -3211,6 +3247,10 @@ def cmd_columns_projections(args: argparse.Namespace) -> None:
3211
3247
  cmd_columns_projections_impl(args, runtime=_column_command_runtime())
3212
3248
 
3213
3249
 
3250
+ def cmd_columns_test_http_request(args: argparse.Namespace) -> None:
3251
+ cmd_columns_test_http_request_impl(args, runtime=_column_command_runtime())
3252
+
3253
+
3214
3254
  def cmd_columns_run(args: argparse.Namespace) -> None:
3215
3255
  cmd_columns_run_impl(args, runtime=_column_command_runtime())
3216
3256
 
@@ -3794,6 +3834,19 @@ def build_parser() -> argparse.ArgumentParser:
3794
3834
  _add_output_formatting_arguments(ppersonalschema)
3795
3835
  ppersonalschema.set_defaults(func=cmd_personal_context_schema)
3796
3836
 
3837
+ register_workspace_secrets_subcommands(
3838
+ sub,
3839
+ add_api_common_arguments=_add_api_common_arguments,
3840
+ add_output_formatting_arguments=_add_output_formatting_arguments,
3841
+ handlers={
3842
+ "list": cmd_workspace_secrets_list,
3843
+ "set": cmd_workspace_secrets_set,
3844
+ "delete": cmd_workspace_secrets_delete,
3845
+ "schema": cmd_workspace_secrets_schema,
3846
+ },
3847
+ workspace_secret_schema_types=WORKSPACE_SECRET_SCHEMA_TYPES,
3848
+ )
3849
+
3797
3850
  # resolved context
3798
3851
  pctx = sub.add_parser("context", help="Resolved requester context used by enrichment/runtime")
3799
3852
  ctx_sub = pctx.add_subparsers(dest="context_cmd", required=True)
@@ -3959,6 +4012,7 @@ def build_parser() -> argparse.ArgumentParser:
3959
4012
  "update": cmd_columns_update,
3960
4013
  "delete": cmd_columns_delete,
3961
4014
  "projections": cmd_columns_projections,
4015
+ "test_http_request": cmd_columns_test_http_request,
3962
4016
  "run": cmd_columns_run,
3963
4017
  "stop": cmd_columns_stop,
3964
4018
  "run_next": cmd_columns_run_next,
@@ -33,6 +33,7 @@ _AUTH_MODE_PREFIXES: Tuple[Tuple[str, str], ...] = (
33
33
  ("org-context.", "user_session"),
34
34
  ("personal-context.", "user_session"),
35
35
  ("context.", "user_session"),
36
+ ("workspace-secrets.", "developer_key_or_user_session"),
36
37
  ("tables.", "developer_key_or_user_session"),
37
38
  ("blacklist.", "developer_key_or_user_session"),
38
39
  ("rows.", "developer_key_or_user_session"),
@@ -200,6 +201,9 @@ def output_contract(command_key: str, parser: argparse.ArgumentParser) -> Option
200
201
 
201
202
 
202
203
  def command_examples(parser: argparse.ArgumentParser) -> List[str]:
204
+ extra_examples = getattr(parser, "_codex_examples", None)
205
+ if isinstance(extra_examples, list) and extra_examples:
206
+ return [str(example) for example in extra_examples if str(example).strip()]
203
207
  usage = parser.format_usage().strip()
204
208
  if usage.startswith("usage: "):
205
209
  usage = usage[len("usage: ") :]
@@ -261,6 +265,8 @@ def collect_cli_command_manifest(
261
265
  "aliases": [],
262
266
  "group": path_parts[1] if len(path_parts) > 1 else None,
263
267
  "help": parser.description or parser.format_usage().strip(),
268
+ "description": parser.description,
269
+ "notes": parser.epilog,
264
270
  "required_flags": required_flags,
265
271
  "positionals": positionals,
266
272
  "options": options,
@@ -338,6 +344,10 @@ def build_cli_reference_markdown(manifest: Dict[str, Any]) -> str:
338
344
  if help_text:
339
345
  lines.append(help_text)
340
346
  lines.append("")
347
+ description = str(entry.get("description") or "").strip()
348
+ if description and description != help_text:
349
+ lines.append(description)
350
+ lines.append("")
341
351
  lines.append(f"- Auth: `{entry.get('auth_mode') or 'unknown'}`")
342
352
  lines.append(f"- Stability: `{entry.get('stability') or 'stable'}`")
343
353
  lines.append(f"- Destructive: `{'yes' if entry.get('destructive') else 'no'}`")
@@ -368,6 +378,13 @@ def build_cli_reference_markdown(manifest: Dict[str, Any]) -> str:
368
378
  lines.append("- Example:")
369
379
  for example in examples:
370
380
  lines.append(f" - `{example}`")
381
+ notes = str(entry.get("notes") or "").strip()
382
+ if notes:
383
+ lines.append("- Notes:")
384
+ lines.append("")
385
+ lines.append("```text")
386
+ lines.extend(notes.rstrip().splitlines())
387
+ lines.append("```")
371
388
  options = entry.get("options") or []
372
389
  if options:
373
390
  lines.append("- Options:")
@@ -22,6 +22,38 @@ class ColumnCommandRuntime:
22
22
  select_next_row_ids: Callable[..., Dict[str, Any]]
23
23
 
24
24
 
25
+ def _normalize_http_request_test_payload(payload: Dict[str, Any]) -> Dict[str, Any]:
26
+ candidate = payload
27
+ config = payload.get("config")
28
+ if isinstance(config, dict):
29
+ candidate = config
30
+ if candidate.get("provider") and str(candidate.get("provider")) != "http_request":
31
+ print("ERROR: columns test-http-request only accepts http_request payloads", file=sys.stderr)
32
+ sys.exit(2)
33
+
34
+ url = candidate.get("url")
35
+ if not isinstance(url, str) or not url.strip():
36
+ print("ERROR: HTTP request preview requires url", file=sys.stderr)
37
+ sys.exit(2)
38
+
39
+ headers = candidate.get("headers")
40
+ if headers is None:
41
+ headers = {}
42
+ if not isinstance(headers, dict):
43
+ print("ERROR: HTTP request preview requires headers to be a JSON object", file=sys.stderr)
44
+ sys.exit(2)
45
+
46
+ timeout = candidate.get("timeout_seconds", candidate.get("timeoutSeconds", 30))
47
+ normalized: Dict[str, Any] = {
48
+ "method": str(candidate.get("method") or "GET").upper(),
49
+ "url": url,
50
+ "headers": headers,
51
+ "body": candidate.get("body"),
52
+ "timeoutSeconds": timeout,
53
+ }
54
+ return normalized
55
+
56
+
25
57
  def cmd_columns_list(args: argparse.Namespace, *, runtime: ColumnCommandRuntime) -> None:
26
58
  token = runtime.resolve_token(args.token, required=True)
27
59
  data = runtime.request_api(
@@ -168,6 +200,30 @@ def cmd_columns_projections(args: argparse.Namespace, *, runtime: ColumnCommandR
168
200
  runtime.print_json(data, args.compact)
169
201
 
170
202
 
203
+ def cmd_columns_test_http_request(args: argparse.Namespace, *, runtime: ColumnCommandRuntime) -> None:
204
+ token = runtime.resolve_token(args.token, required=True)
205
+ payload = runtime.load_json_input(
206
+ inline_json=args.data_json,
207
+ file_path=args.data_file,
208
+ context="data",
209
+ default=None,
210
+ )
211
+ if not isinstance(payload, dict):
212
+ print("ERROR: HTTP request preview requires --data-json/--data-file with a JSON object", file=sys.stderr)
213
+ sys.exit(2)
214
+ data = runtime.request_api(
215
+ "POST",
216
+ f"/api/tables/{args.table_id}/test-http-request",
217
+ base_url=args.base_url,
218
+ token=token,
219
+ use_x_api_key=args.use_x_api_key,
220
+ payload=_normalize_http_request_test_payload(payload),
221
+ timeout=args.timeout,
222
+ verbose=args.verbose,
223
+ )
224
+ runtime.print_json(data, args.compact)
225
+
226
+
171
227
  def cmd_columns_run(args: argparse.Namespace, *, runtime: ColumnCommandRuntime) -> None:
172
228
  token = runtime.resolve_token(args.token, required=True)
173
229
  payload = runtime.normalize_run_payload(args)
@@ -286,13 +342,65 @@ def register_columns_subcommands(
286
342
  add_api_common_arguments(pcd)
287
343
  pcd.set_defaults(func=handlers["delete"])
288
344
 
289
- pcpj = col_sub.add_parser("projections", help="Create JSON projection columns")
345
+ pcpj = col_sub.add_parser(
346
+ "projections",
347
+ help="Create JSON projection columns",
348
+ description=(
349
+ "Create JSON-split projection columns from a JSON source column.\n"
350
+ "Use this command for flattening enrichment JSON output into readable columns.\n"
351
+ "Do not use `autotouch columns create` with `kind: \"projection\"` and empty `config`."
352
+ ),
353
+ epilog=(
354
+ "CreateProjectionsRequest payload example:\n\n"
355
+ "{\n"
356
+ " \"items\": [\n"
357
+ " {\n"
358
+ " \"key\": \"company_name\",\n"
359
+ " \"label\": \"Company Name\",\n"
360
+ " \"sourceColumnId\": \"<JSON_COLUMN_ID>\",\n"
361
+ " \"path\": \"company_name\",\n"
362
+ " \"dataType\": \"text\"\n"
363
+ " }\n"
364
+ " ]\n"
365
+ "}\n\n"
366
+ "Each item must include `key`, `label`, `sourceColumnId`, and `path`."
367
+ ),
368
+ formatter_class=argparse.RawDescriptionHelpFormatter,
369
+ )
290
370
  pcpj.add_argument("--table-id", required=True)
291
371
  pcpj.add_argument("--data-json", help="CreateProjectionsRequest payload JSON")
292
372
  pcpj.add_argument("--data-file", help="CreateProjectionsRequest payload file path")
293
373
  add_api_common_arguments(pcpj)
294
374
  pcpj.set_defaults(func=handlers["projections"])
295
375
 
376
+ pcht = col_sub.add_parser(
377
+ "test-http-request",
378
+ help="Preview an HTTP Request column against the first table row",
379
+ description=(
380
+ "Resolve and execute one HTTP request using the first row in the table.\n"
381
+ "This preview does not write cells.\n"
382
+ "Pass either a full http_request column payload or a direct request config object."
383
+ ),
384
+ epilog=(
385
+ "Recommended flow:\n\n"
386
+ "autotouch columns recipe --type http_request --out-file column.json\n"
387
+ "autotouch columns test-http-request --table-id <TABLE_ID> --data-file column.json\n"
388
+ "autotouch columns create --table-id <TABLE_ID> --data-file column.json\n\n"
389
+ "Template syntax:\n"
390
+ "- {{column_key}} -> row value from the first table row\n"
391
+ "- {{secrets.name}} -> workspace secret or active org integration credential by provider slug"
392
+ ),
393
+ formatter_class=argparse.RawDescriptionHelpFormatter,
394
+ )
395
+ pcht.add_argument("--table-id", required=True)
396
+ pcht.add_argument("--data-json", help="HTTP request column payload JSON or direct preview payload JSON")
397
+ pcht.add_argument("--data-file", help="HTTP request column payload file path or direct preview payload file path")
398
+ add_api_common_arguments(pcht)
399
+ pcht.set_defaults(func=handlers["test_http_request"])
400
+ pcht._codex_examples = [
401
+ "autotouch columns test-http-request --table-id <TABLE_ID> --data-file column.json",
402
+ ]
403
+
296
404
  pcr = col_sub.add_parser("run", help="Run a column")
297
405
  pcr.add_argument("--table-id", required=True)
298
406
  pcr.add_argument("--column-id", required=True)
@@ -0,0 +1,176 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import sys
5
+ from dataclasses import dataclass
6
+ from typing import Any, Callable, Dict, Optional, Sequence
7
+
8
+
9
+ @dataclass(frozen=True)
10
+ class WorkspaceSecretCommandRuntime:
11
+ resolve_token: Callable[[Optional[str], bool], Optional[str]]
12
+ request_api: Callable[..., Any]
13
+ print_json: Callable[[Any, bool], None]
14
+ load_json_input: Callable[..., Any]
15
+ normalize_string_value: Callable[[Any], str]
16
+
17
+
18
+ def _normalize_secret_name(name: Any) -> str:
19
+ normalized = str(name or "").strip().lower()
20
+ if not normalized:
21
+ print("ERROR: workspace secret name is required", file=sys.stderr)
22
+ sys.exit(2)
23
+ return normalized
24
+
25
+
26
+ def _build_workspace_secret_payload(
27
+ args: argparse.Namespace,
28
+ *,
29
+ runtime: WorkspaceSecretCommandRuntime,
30
+ ) -> Dict[str, Any]:
31
+ payload = runtime.load_json_input(
32
+ inline_json=getattr(args, "data_json", None),
33
+ file_path=getattr(args, "data_file", None),
34
+ context="data",
35
+ default=None,
36
+ )
37
+ if payload is not None:
38
+ if not isinstance(payload, dict):
39
+ print("ERROR: workspace-secrets set payload must be a JSON object", file=sys.stderr)
40
+ sys.exit(2)
41
+ else:
42
+ payload = {
43
+ "name": _normalize_secret_name(getattr(args, "name", None)),
44
+ "value": runtime.normalize_string_value(getattr(args, "value", None)),
45
+ }
46
+
47
+ name = _normalize_secret_name(payload.get("name"))
48
+ value = runtime.normalize_string_value(payload.get("value"))
49
+ if not value:
50
+ print("ERROR: workspace secret value is required", file=sys.stderr)
51
+ sys.exit(2)
52
+ return {
53
+ "name": name,
54
+ "value": value,
55
+ }
56
+
57
+
58
+ def cmd_workspace_secrets_list(
59
+ args: argparse.Namespace,
60
+ *,
61
+ runtime: WorkspaceSecretCommandRuntime,
62
+ ) -> None:
63
+ token = runtime.resolve_token(args.token, required=True)
64
+ data = runtime.request_api(
65
+ "GET",
66
+ "/api/workspace-secrets",
67
+ base_url=args.base_url,
68
+ token=token,
69
+ use_x_api_key=args.use_x_api_key,
70
+ timeout=args.timeout,
71
+ verbose=args.verbose,
72
+ )
73
+ runtime.print_json(data, args.compact)
74
+
75
+
76
+ def cmd_workspace_secrets_set(
77
+ args: argparse.Namespace,
78
+ *,
79
+ runtime: WorkspaceSecretCommandRuntime,
80
+ ) -> None:
81
+ token = runtime.resolve_token(args.token, required=True)
82
+ payload = _build_workspace_secret_payload(args, runtime=runtime)
83
+ data = runtime.request_api(
84
+ "POST",
85
+ "/api/workspace-secrets",
86
+ base_url=args.base_url,
87
+ token=token,
88
+ use_x_api_key=args.use_x_api_key,
89
+ payload=payload,
90
+ timeout=args.timeout,
91
+ verbose=args.verbose,
92
+ )
93
+ runtime.print_json(data, args.compact)
94
+
95
+
96
+ def cmd_workspace_secrets_delete(
97
+ args: argparse.Namespace,
98
+ *,
99
+ runtime: WorkspaceSecretCommandRuntime,
100
+ ) -> None:
101
+ if not getattr(args, "yes", False):
102
+ print(
103
+ "ERROR: workspace secret delete is destructive. Re-run with --yes to confirm.",
104
+ file=sys.stderr,
105
+ )
106
+ sys.exit(2)
107
+
108
+ token = runtime.resolve_token(args.token, required=True)
109
+ secret_name = _normalize_secret_name(getattr(args, "name", None))
110
+ data = runtime.request_api(
111
+ "DELETE",
112
+ f"/api/workspace-secrets/{secret_name}",
113
+ base_url=args.base_url,
114
+ token=token,
115
+ use_x_api_key=args.use_x_api_key,
116
+ timeout=args.timeout,
117
+ verbose=args.verbose,
118
+ )
119
+ runtime.print_json(data, args.compact)
120
+
121
+
122
+ def register_workspace_secrets_subcommands(
123
+ subparsers: argparse._SubParsersAction[argparse.ArgumentParser],
124
+ *,
125
+ add_api_common_arguments: Callable[[argparse.ArgumentParser], None],
126
+ add_output_formatting_arguments: Callable[[argparse.ArgumentParser], None],
127
+ handlers: Dict[str, Callable[[argparse.Namespace], None]],
128
+ workspace_secret_schema_types: Sequence[str],
129
+ ) -> None:
130
+ pws = subparsers.add_parser(
131
+ "workspace-secrets",
132
+ help="Manage reusable org-scoped secrets for HTTP request columns",
133
+ description=(
134
+ "Manage reusable org-scoped secrets used by {{secrets.name}} templates in HTTP request columns "
135
+ "and other templated features."
136
+ ),
137
+ epilog=(
138
+ "Developer API keys need secrets:read / secrets:write scopes (or *) for these endpoints.\n"
139
+ "Workspace secrets are optional; HTTP request columns can still use literal headers/body values."
140
+ ),
141
+ formatter_class=argparse.RawDescriptionHelpFormatter,
142
+ )
143
+ pws._codex_examples = [
144
+ "autotouch workspace-secrets set --name clearbit --value \"$CLEARBIT_API_KEY\"",
145
+ "autotouch workspace-secrets list",
146
+ "autotouch workspace-secrets delete --name clearbit --yes",
147
+ ]
148
+ ws_sub = pws.add_subparsers(dest="workspace_secret_cmd", required=True)
149
+
150
+ pwsl = ws_sub.add_parser("list", help="List workspace secrets (masked values)")
151
+ add_api_common_arguments(pwsl)
152
+ pwsl.set_defaults(func=handlers["list"])
153
+
154
+ pwss = ws_sub.add_parser(
155
+ "set",
156
+ help="Create or update a workspace secret",
157
+ description="Create or update a workspace secret used by {{secrets.name}} template references.",
158
+ )
159
+ pwss.add_argument("--name", help="Secret/provider slug, for example clearbit")
160
+ pwss.add_argument("--value", help="Secret value (for example an API key or bearer token)")
161
+ pwss.add_argument("--data-json", help="Explicit workspace secret payload JSON")
162
+ pwss.add_argument("--data-file", help="Path to workspace secret payload JSON file")
163
+ add_api_common_arguments(pwss)
164
+ pwss.set_defaults(func=handlers["set"])
165
+
166
+ pwsd = ws_sub.add_parser("delete", help="Delete a workspace secret")
167
+ pwsd.add_argument("--name", required=True, help="Secret/provider slug to delete")
168
+ pwsd.add_argument("--yes", action="store_true", help="Confirm deletion")
169
+ add_api_common_arguments(pwsd)
170
+ pwsd.set_defaults(func=handlers["delete"])
171
+
172
+ pwsschema = ws_sub.add_parser("schema", help="Print workspace-secrets payload schemas")
173
+ pwsschema.add_argument("--type", choices=["all", *workspace_secret_schema_types], default="all")
174
+ pwsschema.add_argument("--out-file", help="Write schema JSON to file")
175
+ add_output_formatting_arguments(pwsschema)
176
+ pwsschema.set_defaults(func=handlers["schema"])