autotouch-cli 0.2.30__tar.gz → 0.2.31__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 (21) hide show
  1. {autotouch_cli-0.2.30 → autotouch_cli-0.2.31}/PKG-INFO +4 -2
  2. {autotouch_cli-0.2.30 → autotouch_cli-0.2.31}/autotouch_cli/cli.py +140 -21
  3. {autotouch_cli-0.2.30 → autotouch_cli-0.2.31}/autotouch_cli.egg-info/PKG-INFO +4 -2
  4. {autotouch_cli-0.2.30 → autotouch_cli-0.2.31}/docs/research-table/reference/autotouch-cli.md +3 -1
  5. {autotouch_cli-0.2.30 → autotouch_cli-0.2.31}/pyproject.toml +1 -1
  6. {autotouch_cli-0.2.30 → autotouch_cli-0.2.31}/autotouch_cli/__init__.py +0 -0
  7. {autotouch_cli-0.2.30 → autotouch_cli-0.2.31}/autotouch_cli.egg-info/SOURCES.txt +0 -0
  8. {autotouch_cli-0.2.30 → autotouch_cli-0.2.31}/autotouch_cli.egg-info/dependency_links.txt +0 -0
  9. {autotouch_cli-0.2.30 → autotouch_cli-0.2.31}/autotouch_cli.egg-info/entry_points.txt +0 -0
  10. {autotouch_cli-0.2.30 → autotouch_cli-0.2.31}/autotouch_cli.egg-info/requires.txt +0 -0
  11. {autotouch_cli-0.2.30 → autotouch_cli-0.2.31}/autotouch_cli.egg-info/top_level.txt +0 -0
  12. {autotouch_cli-0.2.30 → autotouch_cli-0.2.31}/setup.cfg +0 -0
  13. {autotouch_cli-0.2.30 → autotouch_cli-0.2.31}/tests/test_column_prompt_compiler.py +0 -0
  14. {autotouch_cli-0.2.30 → autotouch_cli-0.2.31}/tests/test_contactout_custom.py +0 -0
  15. {autotouch_cli-0.2.30 → autotouch_cli-0.2.31}/tests/test_contactout_integration.py +0 -0
  16. {autotouch_cli-0.2.30 → autotouch_cli-0.2.31}/tests/test_contactout_multi_titles.py +0 -0
  17. {autotouch_cli-0.2.30 → autotouch_cli-0.2.31}/tests/test_contactout_pipeline.py +0 -0
  18. {autotouch_cli-0.2.30 → autotouch_cli-0.2.31}/tests/test_contactout_simple.py +0 -0
  19. {autotouch_cli-0.2.30 → autotouch_cli-0.2.31}/tests/test_contactout_v2_bulk.py +0 -0
  20. {autotouch_cli-0.2.30 → autotouch_cli-0.2.31}/tests/test_lead_required_fields.py +0 -0
  21. {autotouch_cli-0.2.30 → autotouch_cli-0.2.31}/tests/test_phone_provider_pipeline.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: autotouch-cli
3
- Version: 0.2.30
3
+ Version: 0.2.31
4
4
  Summary: Autotouch Smart Table CLI
5
5
  Requires-Python: >=3.9
6
6
  Description-Content-Type: text/markdown
@@ -71,6 +71,7 @@ Developer keys and scopes are identical to raw API usage (`stk_...`, same scope
71
71
  autotouch auth set-key --api-key stk_... --base-url https://app.autotouch.ai
72
72
  autotouch auth check
73
73
  autotouch auth show
74
+ autotouch auth whoami
74
75
  ```
75
76
 
76
77
  For user/admin endpoints like onboarding, org context, and personal context, sign in with a user session too:
@@ -107,8 +108,9 @@ autotouch auth bootstrap --data-file bootstrap.json --save-key
107
108
 
108
109
  Notes:
109
110
  - New orgs created through signup/bootstrap start with `50` credits.
110
- - `--save-key` stores the returned `apiKey` in local CLI config automatically.
111
+ - `--save-key` stores the returned `apiKey` and also signs in the same user so the saved config has both the developer key and the user session.
111
112
  - Identity linking is email-based: later human sign-in with the same normalized email maps to the same user/org.
113
+ - `auth whoami` shows which user/org the saved session belongs to.
112
114
 
113
115
  ## Onboarding and context
114
116
 
@@ -801,20 +801,73 @@ def _api_url(value: Optional[str] = None) -> str:
801
801
  return DEFAULT_API_URL.rstrip("/")
802
802
 
803
803
 
804
+ def _looks_like_api_key(value: Optional[str]) -> bool:
805
+ token = str(value or "").strip()
806
+ return token.startswith("stk_") and "." in token
807
+
808
+
809
+ def _looks_like_jwt(value: Optional[str]) -> bool:
810
+ token = str(value or "").strip()
811
+ return bool(token) and not _looks_like_api_key(token) and token.count(".") == 2
812
+
813
+
814
+ def _env_user_token() -> Optional[str]:
815
+ for key in ("SMARTTABLE_TOKEN", "AUTOTOUCH_TOKEN"):
816
+ value = str(os.environ.get(key) or "").strip()
817
+ if value and not _looks_like_api_key(value):
818
+ return value
819
+ return None
820
+
821
+
822
+ def _env_api_key() -> Optional[str]:
823
+ direct = str(os.environ.get("AUTOTOUCH_API_KEY") or "").strip()
824
+ if direct:
825
+ return direct
826
+ for key in ("SMARTTABLE_TOKEN", "AUTOTOUCH_TOKEN"):
827
+ value = str(os.environ.get(key) or "").strip()
828
+ if _looks_like_api_key(value):
829
+ return value
830
+ return None
831
+
832
+
804
833
  def _resolve_token(explicit_token: Optional[str], required: bool = True) -> Optional[str]:
805
- tok = (
806
- explicit_token
807
- or os.environ.get("SMARTTABLE_TOKEN")
808
- or os.environ.get("AUTOTOUCH_API_KEY")
809
- or os.environ.get("AUTOTOUCH_TOKEN")
810
- )
834
+ tok = str(explicit_token or "").strip()
835
+ if not tok:
836
+ cfg = _load_config()
837
+ tok = (
838
+ str(cfg.get("auth_token") or "").strip()
839
+ or str(cfg.get("api_key") or "").strip()
840
+ or str(_env_user_token() or "").strip()
841
+ or str(_env_api_key() or "").strip()
842
+ )
843
+ if not tok:
844
+ if required:
845
+ print(
846
+ "ERROR: missing token. Pass --token, run `autotouch auth login`, or run `autotouch auth set-key`.",
847
+ file=sys.stderr,
848
+ )
849
+ sys.exit(2)
850
+ return None
851
+ return tok
852
+
853
+
854
+ def _resolve_user_session_token(explicit_token: Optional[str], required: bool = True) -> Optional[str]:
855
+ tok = str(explicit_token or "").strip()
856
+ if tok and _looks_like_api_key(tok):
857
+ print(
858
+ "ERROR: this command requires a user session token, not a developer API key. Run `autotouch auth login`.",
859
+ file=sys.stderr,
860
+ )
861
+ sys.exit(2)
862
+
811
863
  if not tok:
812
864
  cfg = _load_config()
813
- tok = cfg.get("auth_token") or cfg.get("api_key")
865
+ tok = str(cfg.get("auth_token") or "").strip() or str(_env_user_token() or "").strip()
866
+
814
867
  if not tok:
815
868
  if required:
816
869
  print(
817
- "ERROR: missing token. Pass --token, set SMARTTABLE_TOKEN/AUTOTOUCH_API_KEY, or run `autotouch auth login` / `autotouch auth set-key`.",
870
+ "ERROR: this command requires a saved user session. Run `autotouch auth login` or pass a JWT with --token.",
818
871
  file=sys.stderr,
819
872
  )
820
873
  sys.exit(2)
@@ -3109,16 +3162,53 @@ def cmd_auth_bootstrap(args: argparse.Namespace) -> None:
3109
3162
  output: Any = data
3110
3163
  if getattr(args, "save_key", False) and isinstance(data, dict):
3111
3164
  api_key = str(data.get("apiKey") or data.get("api_key") or "").strip()
3165
+ email = str(payload.get("email") or "").strip().lower()
3166
+ password = str(payload.get("password") or "").strip()
3167
+ session_data: Optional[Dict[str, Any]] = None
3168
+ session_error: Optional[str] = None
3169
+
3170
+ if email and password:
3171
+ try:
3172
+ session_data = _request_api(
3173
+ "POST",
3174
+ "/api/auth/login",
3175
+ base_url=args.base_url,
3176
+ token=None,
3177
+ use_x_api_key=False,
3178
+ payload={"email": email, "password": password},
3179
+ timeout=args.timeout,
3180
+ verbose=args.verbose,
3181
+ )
3182
+ except SystemExit as exc:
3183
+ session_error = f"login failed with exit code {exc.code}"
3184
+ except Exception as exc:
3185
+ session_error = str(exc)
3186
+
3112
3187
  if api_key:
3113
3188
  cfg = _load_config()
3114
3189
  cfg["api_key"] = api_key
3190
+ access_token = _normalize_string_value((session_data or {}).get("access_token"))
3191
+ refresh_token = _normalize_string_value((session_data or {}).get("refresh_token"))
3192
+ if access_token:
3193
+ cfg["auth_token"] = access_token
3194
+ cfg["auth_email"] = email
3195
+ if refresh_token:
3196
+ cfg["refresh_token"] = refresh_token
3115
3197
  cfg["base_url"] = str(args.base_url or _api_url()).rstrip("/")
3116
3198
  cfg["updated_at_epoch"] = int(time.time())
3117
3199
  config_path = _save_config(cfg)
3118
3200
  output = dict(data)
3119
3201
  output["saved"] = True
3202
+ output["saved_api_key"] = True
3203
+ output["saved_session"] = bool(access_token)
3120
3204
  output["config_path"] = str(config_path)
3121
3205
  output["api_key_masked"] = _mask_api_key(api_key)
3206
+ if access_token:
3207
+ output["access_token_masked"] = _mask_secret(access_token)
3208
+ if refresh_token:
3209
+ output["refresh_token_masked"] = _mask_secret(refresh_token)
3210
+ if session_error:
3211
+ output["session_warning"] = session_error
3122
3212
  else:
3123
3213
  output = dict(data)
3124
3214
  output["saved"] = False
@@ -3202,6 +3292,9 @@ def cmd_auth_show(args: argparse.Namespace) -> None:
3202
3292
  refresh_token = str(cfg.get("refresh_token") or "").strip()
3203
3293
  base_url = str(cfg.get("base_url") or _api_url()).rstrip("/")
3204
3294
  path = _config_path()
3295
+ default_general_auth = "auth_token" if auth_token else ("api_key" if api_key else None)
3296
+ default_user_session_auth = "auth_token" if auth_token else ("env_token" if _env_user_token() else None)
3297
+ default_developer_auth = "api_key" if api_key else ("env_api_key" if _env_api_key() else None)
3205
3298
  output = {
3206
3299
  "config_path": str(path),
3207
3300
  "config_exists": path.exists(),
@@ -3214,10 +3307,27 @@ def cmd_auth_show(args: argparse.Namespace) -> None:
3214
3307
  "auth_email": cfg.get("auth_email"),
3215
3308
  "base_url": base_url,
3216
3309
  "updated_at_epoch": cfg.get("updated_at_epoch"),
3310
+ "default_general_auth": default_general_auth,
3311
+ "default_user_session_auth": default_user_session_auth,
3312
+ "default_developer_auth": default_developer_auth,
3217
3313
  }
3218
3314
  _print_json(output, compact=args.compact)
3219
3315
 
3220
3316
 
3317
+ def cmd_auth_whoami(args: argparse.Namespace) -> None:
3318
+ token = _resolve_user_session_token(args.token, required=True)
3319
+ data = _request_api(
3320
+ "GET",
3321
+ "/api/auth/me",
3322
+ base_url=args.base_url,
3323
+ token=token,
3324
+ use_x_api_key=False,
3325
+ timeout=args.timeout,
3326
+ verbose=args.verbose,
3327
+ )
3328
+ _print_json(data, compact=args.compact)
3329
+
3330
+
3221
3331
  def cmd_auth_clear(args: argparse.Namespace) -> None:
3222
3332
  cfg = _load_config()
3223
3333
  changed = False
@@ -3355,7 +3465,7 @@ def _build_personal_context_payload(args: argparse.Namespace) -> Dict[str, Any]:
3355
3465
 
3356
3466
 
3357
3467
  def cmd_onboarding_submit_domain(args: argparse.Namespace) -> None:
3358
- token = _resolve_token(args.token, required=True)
3468
+ token = _resolve_user_session_token(args.token, required=True)
3359
3469
  payload = _load_json_input(
3360
3470
  inline_json=getattr(args, "data_json", None),
3361
3471
  file_path=getattr(args, "data_file", None),
@@ -3377,7 +3487,7 @@ def cmd_onboarding_submit_domain(args: argparse.Namespace) -> None:
3377
3487
  "/api/onboarding/submit-domain",
3378
3488
  base_url=args.base_url,
3379
3489
  token=token,
3380
- use_x_api_key=args.use_x_api_key,
3490
+ use_x_api_key=False,
3381
3491
  form_payload={
3382
3492
  "website": str(payload.get("website") or payload.get("domain") or "").strip(),
3383
3493
  "domain": str(payload.get("domain") or "").strip(),
@@ -3389,13 +3499,13 @@ def cmd_onboarding_submit_domain(args: argparse.Namespace) -> None:
3389
3499
 
3390
3500
 
3391
3501
  def cmd_org_context_get(args: argparse.Namespace) -> None:
3392
- token = _resolve_token(args.token, required=True)
3502
+ token = _resolve_user_session_token(args.token, required=True)
3393
3503
  data = _request_api(
3394
3504
  "GET",
3395
3505
  "/api/org/context",
3396
3506
  base_url=args.base_url,
3397
3507
  token=token,
3398
- use_x_api_key=args.use_x_api_key,
3508
+ use_x_api_key=False,
3399
3509
  timeout=args.timeout,
3400
3510
  verbose=args.verbose,
3401
3511
  )
@@ -3403,14 +3513,14 @@ def cmd_org_context_get(args: argparse.Namespace) -> None:
3403
3513
 
3404
3514
 
3405
3515
  def cmd_org_context_set(args: argparse.Namespace) -> None:
3406
- token = _resolve_token(args.token, required=True)
3516
+ token = _resolve_user_session_token(args.token, required=True)
3407
3517
  payload = _build_org_context_payload(args)
3408
3518
  data = _request_api(
3409
3519
  "POST",
3410
3520
  "/api/org/context",
3411
3521
  base_url=args.base_url,
3412
3522
  token=token,
3413
- use_x_api_key=args.use_x_api_key,
3523
+ use_x_api_key=False,
3414
3524
  form_payload=_org_context_form_payload(payload),
3415
3525
  timeout=args.timeout,
3416
3526
  verbose=args.verbose,
@@ -3419,13 +3529,13 @@ def cmd_org_context_set(args: argparse.Namespace) -> None:
3419
3529
 
3420
3530
 
3421
3531
  def cmd_personal_context_get(args: argparse.Namespace) -> None:
3422
- token = _resolve_token(args.token, required=True)
3532
+ token = _resolve_user_session_token(args.token, required=True)
3423
3533
  data = _request_api(
3424
3534
  "GET",
3425
3535
  "/api/onboarding/personal-context",
3426
3536
  base_url=args.base_url,
3427
3537
  token=token,
3428
- use_x_api_key=args.use_x_api_key,
3538
+ use_x_api_key=False,
3429
3539
  timeout=args.timeout,
3430
3540
  verbose=args.verbose,
3431
3541
  )
@@ -3433,14 +3543,14 @@ def cmd_personal_context_get(args: argparse.Namespace) -> None:
3433
3543
 
3434
3544
 
3435
3545
  def cmd_personal_context_set(args: argparse.Namespace) -> None:
3436
- token = _resolve_token(args.token, required=True)
3546
+ token = _resolve_user_session_token(args.token, required=True)
3437
3547
  payload = _build_personal_context_payload(args)
3438
3548
  data = _request_api(
3439
3549
  "POST",
3440
3550
  "/api/onboarding/personal-context",
3441
3551
  base_url=args.base_url,
3442
3552
  token=token,
3443
- use_x_api_key=args.use_x_api_key,
3553
+ use_x_api_key=False,
3444
3554
  payload=payload,
3445
3555
  timeout=args.timeout,
3446
3556
  verbose=args.verbose,
@@ -3449,13 +3559,13 @@ def cmd_personal_context_set(args: argparse.Namespace) -> None:
3449
3559
 
3450
3560
 
3451
3561
  def cmd_context_resolved(args: argparse.Namespace) -> None:
3452
- token = _resolve_token(args.token, required=True)
3562
+ token = _resolve_user_session_token(args.token, required=True)
3453
3563
  data = _request_api(
3454
3564
  "GET",
3455
3565
  "/api/llm/company-context",
3456
3566
  base_url=args.base_url,
3457
3567
  token=token,
3458
- use_x_api_key=args.use_x_api_key,
3568
+ use_x_api_key=False,
3459
3569
  timeout=args.timeout,
3460
3570
  verbose=args.verbose,
3461
3571
  )
@@ -6290,7 +6400,7 @@ def build_parser() -> argparse.ArgumentParser:
6290
6400
  pab.add_argument("--scopes-file", help="Optional JSON file with scopes array")
6291
6401
  pab.add_argument("--data-json", help="Explicit agent bootstrap payload JSON")
6292
6402
  pab.add_argument("--data-file", help="Path to agent bootstrap payload JSON file")
6293
- pab.add_argument("--save-key", action="store_true", help="Persist the returned apiKey into local CLI config")
6403
+ pab.add_argument("--save-key", action="store_true", help="Persist the returned apiKey and bootstrap a saved JWT session")
6294
6404
  pab.add_argument("--base-url", default=_api_url(), help=f"API base URL (default: {DEFAULT_API_URL})")
6295
6405
  pab.add_argument("--timeout", type=int, default=DEFAULT_TIMEOUT_SECONDS, help="HTTP timeout in seconds")
6296
6406
  pab.add_argument("--output", choices=["json", "human"], default="json", help="Output mode")
@@ -6325,6 +6435,15 @@ def build_parser() -> argparse.ArgumentParser:
6325
6435
  pashow.add_argument("--compact", action="store_true", help="Print compact JSON")
6326
6436
  pashow.set_defaults(func=cmd_auth_show)
6327
6437
 
6438
+ pawho = auth_sub.add_parser("whoami", help="Show the current saved user session identity")
6439
+ pawho.add_argument("--token", help="Explicit JWT session token")
6440
+ pawho.add_argument("--base-url", default=_api_url(), help=f"API base URL (default: {DEFAULT_API_URL})")
6441
+ pawho.add_argument("--timeout", type=int, default=DEFAULT_TIMEOUT_SECONDS, help="HTTP timeout in seconds")
6442
+ pawho.add_argument("--output", choices=["json", "human"], default="json", help="Output mode")
6443
+ pawho.add_argument("--compact", action="store_true", help="Print compact JSON")
6444
+ pawho.add_argument("--verbose", action="store_true", help="Print request metadata to stderr")
6445
+ pawho.set_defaults(func=cmd_auth_whoami)
6446
+
6328
6447
  paclear = auth_sub.add_parser("clear", help="Clear stored auth config")
6329
6448
  paclear.add_argument("--all", action="store_true", help="Clear all config keys")
6330
6449
  paclear.add_argument("--clear-base-url", action="store_true", help="Also clear stored base_url")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: autotouch-cli
3
- Version: 0.2.30
3
+ Version: 0.2.31
4
4
  Summary: Autotouch Smart Table CLI
5
5
  Requires-Python: >=3.9
6
6
  Description-Content-Type: text/markdown
@@ -71,6 +71,7 @@ Developer keys and scopes are identical to raw API usage (`stk_...`, same scope
71
71
  autotouch auth set-key --api-key stk_... --base-url https://app.autotouch.ai
72
72
  autotouch auth check
73
73
  autotouch auth show
74
+ autotouch auth whoami
74
75
  ```
75
76
 
76
77
  For user/admin endpoints like onboarding, org context, and personal context, sign in with a user session too:
@@ -107,8 +108,9 @@ autotouch auth bootstrap --data-file bootstrap.json --save-key
107
108
 
108
109
  Notes:
109
110
  - New orgs created through signup/bootstrap start with `50` credits.
110
- - `--save-key` stores the returned `apiKey` in local CLI config automatically.
111
+ - `--save-key` stores the returned `apiKey` and also signs in the same user so the saved config has both the developer key and the user session.
111
112
  - Identity linking is email-based: later human sign-in with the same normalized email maps to the same user/org.
113
+ - `auth whoami` shows which user/org the saved session belongs to.
112
114
 
113
115
  ## Onboarding and context
114
116
 
@@ -62,6 +62,7 @@ Developer keys and scopes are identical to raw API usage (`stk_...`, same scope
62
62
  autotouch auth set-key --api-key stk_... --base-url https://app.autotouch.ai
63
63
  autotouch auth check
64
64
  autotouch auth show
65
+ autotouch auth whoami
65
66
  ```
66
67
 
67
68
  For user/admin endpoints like onboarding, org context, and personal context, sign in with a user session too:
@@ -98,8 +99,9 @@ autotouch auth bootstrap --data-file bootstrap.json --save-key
98
99
 
99
100
  Notes:
100
101
  - New orgs created through signup/bootstrap start with `50` credits.
101
- - `--save-key` stores the returned `apiKey` in local CLI config automatically.
102
+ - `--save-key` stores the returned `apiKey` and also signs in the same user so the saved config has both the developer key and the user session.
102
103
  - Identity linking is email-based: later human sign-in with the same normalized email maps to the same user/org.
104
+ - `auth whoami` shows which user/org the saved session belongs to.
103
105
 
104
106
  ## Onboarding and context
105
107
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "autotouch-cli"
7
- version = "0.2.30"
7
+ version = "0.2.31"
8
8
  description = "Autotouch Smart Table CLI"
9
9
  readme = "docs/research-table/reference/autotouch-cli.md"
10
10
  requires-python = ">=3.9"
File without changes