autotouch-cli 0.2.88__tar.gz → 0.2.90__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 (55) hide show
  1. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/PKG-INFO +2 -2
  2. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/README.md +1 -1
  3. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_cli/data/CLI_REFERENCE.md +1 -1
  4. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_cli/data/cli-manifest.json +1 -1
  5. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_cli/templates.py +183 -17
  6. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_cli.egg-info/PKG-INFO +2 -2
  7. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_shared/provider_registry.py +45 -0
  8. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/pyproject.toml +1 -1
  9. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/MANIFEST.in +0 -0
  10. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_cli/__init__.py +0 -0
  11. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_cli/cli.py +0 -0
  12. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_cli/cli_contracts.py +0 -0
  13. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_cli/commands/__init__.py +0 -0
  14. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_cli/commands/agents.py +0 -0
  15. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_cli/commands/auth.py +0 -0
  16. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_cli/commands/cells.py +0 -0
  17. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_cli/commands/columns.py +0 -0
  18. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_cli/commands/jobs.py +0 -0
  19. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_cli/commands/leads.py +0 -0
  20. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_cli/commands/linkedin.py +0 -0
  21. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_cli/commands/list_build.py +0 -0
  22. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_cli/commands/prompts.py +0 -0
  23. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_cli/commands/rows.py +0 -0
  24. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_cli/commands/search.py +0 -0
  25. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_cli/commands/sequences.py +0 -0
  26. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_cli/commands/tables.py +0 -0
  27. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_cli/commands/tasks.py +0 -0
  28. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_cli/commands/webhooks.py +0 -0
  29. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_cli/commands/workspace_secrets.py +0 -0
  30. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_cli/core/__init__.py +0 -0
  31. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_cli/core/auth.py +0 -0
  32. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_cli/core/config.py +0 -0
  33. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_cli/core/csv_import.py +0 -0
  34. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_cli/core/http.py +0 -0
  35. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_cli/core/io.py +0 -0
  36. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_cli/core/output.py +0 -0
  37. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_cli/core/polling.py +0 -0
  38. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_cli/core/run.py +0 -0
  39. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_cli/core/validation.py +0 -0
  40. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_cli/exceptions.py +0 -0
  41. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_cli/mongo_status.py +0 -0
  42. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_cli/parser.py +0 -0
  43. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_cli/parser_groups.py +0 -0
  44. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_cli/sequence_support.py +0 -0
  45. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_cli.egg-info/SOURCES.txt +0 -0
  46. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_cli.egg-info/dependency_links.txt +0 -0
  47. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_cli.egg-info/entry_points.txt +0 -0
  48. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_cli.egg-info/requires.txt +0 -0
  49. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_cli.egg-info/top_level.txt +0 -0
  50. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_shared/__init__.py +0 -0
  51. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_shared/linkedin_contract.py +0 -0
  52. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_shared/linkedin_filters.py +0 -0
  53. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_shared/list_build_contract.py +0 -0
  54. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/autotouch_shared/search_contract.py +0 -0
  55. {autotouch_cli-0.2.88 → autotouch_cli-0.2.90}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: autotouch-cli
3
- Version: 0.2.88
3
+ Version: 0.2.90
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
@@ -178,7 +178,7 @@ For automation or agent-driven setup, use:
178
178
  - `autotouch cli-manifest --output json` for the local machine-readable command contract
179
179
  - `autotouch cli-reference` for the shipped parser-generated reference
180
180
  - `autotouch capabilities --output json` for provider/workflow contracts
181
- - `autotouch --version` should be `0.2.88` or newer for structured company list builds without user-supplied keywords, stored list-build input lookup, research-workspace list-build guidance, the cleaned single LinkedIn-sourced list-build path, Exa Company Search up to 100 results, and scheduled agent `--target ACCOUNTS|LEADS`
181
+ - `autotouch --version` should be `0.2.90` or newer for structured company list builds without user-supplied keywords, stored list-build input lookup, research-workspace list-build guidance, the cleaned single LinkedIn-sourced list-build path, Exa Company Search up to 100 results, scheduled agent `--target ACCOUNTS|LEADS`, paced HTTP Request column contracts, and V2 sequence flowGraph recipes
182
182
  - `autotouch capabilities --output json --select list_builds` for documented list-build inputs such as geography IDs, company size buckets, profile language, and company IDs
183
183
  - `autotouch list-build inputs` and `autotouch list-build pricing` before creating durable list-build jobs
184
184
  - `autotouch list-build companies` and `autotouch list-build leads` for durable LinkedIn-sourced company and lead list builds with Smart Table-owned background workers, visible progress, and no user-owned LinkedIn connection requirement
@@ -153,7 +153,7 @@ For automation or agent-driven setup, use:
153
153
  - `autotouch cli-manifest --output json` for the local machine-readable command contract
154
154
  - `autotouch cli-reference` for the shipped parser-generated reference
155
155
  - `autotouch capabilities --output json` for provider/workflow contracts
156
- - `autotouch --version` should be `0.2.88` or newer for structured company list builds without user-supplied keywords, stored list-build input lookup, research-workspace list-build guidance, the cleaned single LinkedIn-sourced list-build path, Exa Company Search up to 100 results, and scheduled agent `--target ACCOUNTS|LEADS`
156
+ - `autotouch --version` should be `0.2.90` or newer for structured company list builds without user-supplied keywords, stored list-build input lookup, research-workspace list-build guidance, the cleaned single LinkedIn-sourced list-build path, Exa Company Search up to 100 results, scheduled agent `--target ACCOUNTS|LEADS`, paced HTTP Request column contracts, and V2 sequence flowGraph recipes
157
157
  - `autotouch capabilities --output json --select list_builds` for documented list-build inputs such as geography IDs, company size buckets, profile language, and company IDs
158
158
  - `autotouch list-build inputs` and `autotouch list-build pricing` before creating durable list-build jobs
159
159
  - `autotouch list-build companies` and `autotouch list-build leads` for durable LinkedIn-sourced company and lead list builds with Smart Table-owned background workers, visible progress, and no user-owned LinkedIn connection requirement
@@ -1,6 +1,6 @@
1
1
  # Autotouch CLI Reference
2
2
 
3
- Generated from the installed parser for `autotouch-cli` `0.2.88`.
3
+ Generated from the installed parser for `autotouch-cli` `0.2.90`.
4
4
  Manifest schema version: `2`.
5
5
 
6
6
  ## Output Modes
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.2.88",
2
+ "version": "0.2.90",
3
3
  "manifest_schema_version": 2,
4
4
  "entry_points": {
5
5
  "autotouch": "autotouch_cli.cli:main",
@@ -199,7 +199,7 @@ SEQUENCE_RECIPE_TYPES = [
199
199
 
200
200
  SEQUENCE_RECIPES: Dict[str, Dict[str, Any]] = {
201
201
  "create": {
202
- "name": "Outbound v1",
202
+ "name": "Outbound flow",
203
203
  "sourceTableId": "<TABLE_ID>",
204
204
  "sourceTableName": "ICP Prospects",
205
205
  "exitRules": {
@@ -243,20 +243,37 @@ SEQUENCE_RECIPES: Dict[str, Dict[str, Any]] = {
243
243
  "aiDraft": True,
244
244
  "scriptTemplate": "",
245
245
  },
246
- {
247
- "id": "linkedin_1",
248
- "kind": "LINKEDIN_CHAT_MESSAGE",
249
- "waitDays": 2,
250
- "waitHours": 0,
251
- "waitMinutes": 0,
252
- "executionMode": "AUTOMATED",
253
- "aiDraft": True,
254
- "scriptTemplate": "",
255
- },
256
246
  ],
247
+ "flowGraph": {
248
+ "version": 1,
249
+ "nodes": [
250
+ {
251
+ "id": "step:email_1",
252
+ "kind": "action",
253
+ "title": "Send email",
254
+ "sourceStepId": "email_1",
255
+ "position": {"x": 0, "y": 0},
256
+ },
257
+ {
258
+ "id": "step:call_1",
259
+ "kind": "action",
260
+ "title": "Call",
261
+ "sourceStepId": "call_1",
262
+ "position": {"x": 0, "y": 160},
263
+ },
264
+ ],
265
+ "edges": [
266
+ {
267
+ "id": "edge:email_1:call_1",
268
+ "source": "step:email_1",
269
+ "target": "step:call_1",
270
+ "kind": "next",
271
+ },
272
+ ],
273
+ },
257
274
  },
258
275
  "bulk_automated": {
259
- "name": "Bulk outbound v1",
276
+ "name": "Bulk outbound flow",
260
277
  "sourceTableId": "<TABLE_ID>",
261
278
  "sourceTableName": "ICP Prospects",
262
279
  "exitRules": {
@@ -302,9 +319,36 @@ SEQUENCE_RECIPES: Dict[str, Dict[str, Any]] = {
302
319
  "bodyTemplate": "Hi {{first_name}}, circling back on this.",
303
320
  },
304
321
  ],
322
+ "flowGraph": {
323
+ "version": 1,
324
+ "nodes": [
325
+ {
326
+ "id": "step:email_1",
327
+ "kind": "action",
328
+ "title": "Send first email",
329
+ "sourceStepId": "email_1",
330
+ "position": {"x": 0, "y": 0},
331
+ },
332
+ {
333
+ "id": "step:email_2",
334
+ "kind": "action",
335
+ "title": "Follow-up email",
336
+ "sourceStepId": "email_2",
337
+ "position": {"x": 0, "y": 160},
338
+ },
339
+ ],
340
+ "edges": [
341
+ {
342
+ "id": "edge:email_1:email_2",
343
+ "source": "step:email_1",
344
+ "target": "step:email_2",
345
+ "kind": "next",
346
+ },
347
+ ],
348
+ },
305
349
  },
306
350
  "linkedin_outreach": {
307
- "name": "LinkedIn outreach v1",
351
+ "name": "LinkedIn outreach flow",
308
352
  "sourceTableId": "<TABLE_ID>",
309
353
  "sourceTableName": "ICP Prospects",
310
354
  "exitRules": {
@@ -342,10 +386,24 @@ SEQUENCE_RECIPES: Dict[str, Dict[str, Any]] = {
342
386
  "aiDraft": True,
343
387
  "scriptTemplate": "",
344
388
  },
389
+ {
390
+ "id": "email_fallback_1",
391
+ "kind": "EMAIL",
392
+ "waitDays": 0,
393
+ "waitHours": 0,
394
+ "waitMinutes": 0,
395
+ "executionMode": "AUTOMATED",
396
+ "emailSendMode": "AUTOMATED",
397
+ "emailIsReply": False,
398
+ "appendSignature": True,
399
+ "aiDraft": True,
400
+ "subjectTemplate": "",
401
+ "bodyTemplate": "",
402
+ },
345
403
  {
346
404
  "id": "message_1",
347
405
  "kind": "LINKEDIN_CHAT_MESSAGE",
348
- "waitDays": 2,
406
+ "waitDays": 0,
349
407
  "waitHours": 0,
350
408
  "waitMinutes": 0,
351
409
  "executionMode": "AUTOMATED",
@@ -353,6 +411,110 @@ SEQUENCE_RECIPES: Dict[str, Dict[str, Any]] = {
353
411
  "scriptTemplate": "",
354
412
  },
355
413
  ],
414
+ "flowGraph": {
415
+ "version": 1,
416
+ "nodes": [
417
+ {
418
+ "id": "step:visit_1",
419
+ "kind": "action",
420
+ "title": "Visit profile",
421
+ "sourceStepId": "visit_1",
422
+ "position": {"x": 0, "y": 0},
423
+ },
424
+ {
425
+ "id": "step:connect_1",
426
+ "kind": "action",
427
+ "title": "Connect",
428
+ "sourceStepId": "connect_1",
429
+ "position": {"x": 0, "y": 160},
430
+ },
431
+ {
432
+ "id": "condition:connect_1:accepted",
433
+ "kind": "condition",
434
+ "title": "Wait for connection accepted",
435
+ "parentNodeId": "step:connect_1",
436
+ "metadata": {
437
+ "decisionType": "outcome_wait",
438
+ "listenFor": "linkedin_connection_accepted",
439
+ "waitMode": "up_to",
440
+ "waitDays": 7,
441
+ "waitHours": 0,
442
+ "waitMinutes": 0,
443
+ },
444
+ "position": {"x": 0, "y": 320},
445
+ },
446
+ {
447
+ "id": "branch:connect_1:accepted",
448
+ "kind": "branch",
449
+ "branchId": "connected",
450
+ "title": "Connected",
451
+ "parentNodeId": "condition:connect_1:accepted",
452
+ "position": {"x": -180, "y": 480},
453
+ },
454
+ {
455
+ "id": "branch:connect_1:timeout",
456
+ "kind": "branch",
457
+ "branchId": "not-connected",
458
+ "title": "Not connected after 7 days",
459
+ "parentNodeId": "condition:connect_1:accepted",
460
+ "position": {"x": 180, "y": 480},
461
+ },
462
+ {
463
+ "id": "step:message_1",
464
+ "kind": "action",
465
+ "title": "LinkedIn message",
466
+ "parentNodeId": "branch:connect_1:accepted",
467
+ "sourceStepId": "message_1",
468
+ "position": {"x": -180, "y": 640},
469
+ },
470
+ {
471
+ "id": "step:email_fallback_1",
472
+ "kind": "action",
473
+ "title": "Fallback email",
474
+ "parentNodeId": "branch:connect_1:timeout",
475
+ "sourceStepId": "email_fallback_1",
476
+ "position": {"x": 180, "y": 640},
477
+ },
478
+ ],
479
+ "edges": [
480
+ {
481
+ "id": "edge:visit_1:connect_1",
482
+ "source": "step:visit_1",
483
+ "target": "step:connect_1",
484
+ "kind": "next",
485
+ },
486
+ {
487
+ "id": "edge:connect_1:accepted_condition",
488
+ "source": "step:connect_1",
489
+ "target": "condition:connect_1:accepted",
490
+ "kind": "condition",
491
+ },
492
+ {
493
+ "id": "edge:accepted_condition:connected",
494
+ "source": "condition:connect_1:accepted",
495
+ "target": "branch:connect_1:accepted",
496
+ "kind": "branch",
497
+ },
498
+ {
499
+ "id": "edge:accepted_condition:timeout",
500
+ "source": "condition:connect_1:accepted",
501
+ "target": "branch:connect_1:timeout",
502
+ "kind": "branch",
503
+ },
504
+ {
505
+ "id": "edge:connected:message_1",
506
+ "source": "branch:connect_1:accepted",
507
+ "target": "step:message_1",
508
+ "kind": "branch",
509
+ },
510
+ {
511
+ "id": "edge:timeout:email_fallback_1",
512
+ "source": "branch:connect_1:timeout",
513
+ "target": "step:email_fallback_1",
514
+ "kind": "branch",
515
+ },
516
+ ],
517
+ },
356
518
  },
357
519
  "enroll": {
358
520
  "leadIds": ["<LEAD_ID_1>", "<LEAD_ID_2>"],
@@ -365,25 +527,28 @@ SEQUENCE_RECIPES: Dict[str, Dict[str, Any]] = {
365
527
 
366
528
  SEQUENCE_RECIPE_NOTES: Dict[str, List[str]] = {
367
529
  "create": [
368
- "Recommended default: personal delivery plus AUTOMATED email with aiDraft=true.",
530
+ "Recommended default: V2 flowGraph with personal delivery plus AUTOMATED email with aiDraft=true.",
531
+ "flowGraph.version=1 is the executable authoring layer; keep steps aligned with graph action sourceStepId values.",
369
532
  "AI draft generates subject + body at execution time.",
370
- "AI draft for LinkedIn message generates message copy; AI draft for call generates a call script.",
533
+ "AI draft for call generates a call script.",
371
534
  "External sequence create/update requires sourceTableId.",
372
535
  "Pass --actor-user-id on mutating commands when you need explicit actor resolution.",
373
536
  ],
374
537
  "bulk_automated": [
538
+ "Emits a V2 flowGraph with two automated email actions.",
375
539
  "Use this when the provider should send email automatically at scale.",
376
540
  "Bulk delivery sequences cannot contain MANUAL email steps, so aiDraft does not apply to those email steps.",
377
541
  "Activate only after provider readiness is configured (autotouch sequences delivery-status).",
378
542
  ],
379
543
  "linkedin_outreach": [
380
- "LinkedIn outreach sequence: visit profile, connect, then message.",
544
+ "Emits a V2 flowGraph: visit profile, connect, wait for accepted connection, then branch to LinkedIn message or fallback email.",
381
545
  "Supported step kinds: LINKEDIN_VISIT_PROFILE, LINKEDIN_CONNECT, LINKEDIN_CHAT_MESSAGE, LINKEDIN_VOICE_MESSAGE.",
382
546
  "LINKEDIN_VISIT_PROFILE is always AUTOMATED and visits the lead's profile (notifies by default).",
383
547
  "LINKEDIN_CONNECT sends a connection request; scriptTemplate is the invite note (max 300 chars).",
384
548
  "LINKEDIN_CHAT_MESSAGE sends a direct message; uses InMail credits for 2nd/3rd degree connections.",
385
549
  "LINKEDIN_VOICE_MESSAGE sends a voice note; only works for 1st degree connections.",
386
550
  "Connection degree (1st/2nd/3rd/out_of_network) is auto-detected from the LinkedIn profile.",
551
+ "The connected branch is explicit in flowGraph; the fallback email branch is explicit and does not happen silently.",
387
552
  "Set stopOnLinkedInReply=true in exitRules to stop the sequence when the lead replies on LinkedIn.",
388
553
  "Leads must have a linkedin_url field to be reachable for LinkedIn steps.",
389
554
  ],
@@ -1787,6 +1952,7 @@ def emit_sequences_recipe(args: argparse.Namespace, runtime: TemplateRuntime) ->
1787
1952
  usage_lookup={
1788
1953
  "create": "autotouch sequences create --data-file <payload.json>",
1789
1954
  "bulk_automated": "autotouch sequences create --data-file <payload.json>",
1955
+ "linkedin_outreach": "autotouch sequences create --data-file <payload.json>",
1790
1956
  "enroll": "autotouch sequences enroll --sequence-id <SEQUENCE_ID> --data-file <payload.json>",
1791
1957
  },
1792
1958
  label="recipe",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: autotouch-cli
3
- Version: 0.2.88
3
+ Version: 0.2.90
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
@@ -178,7 +178,7 @@ For automation or agent-driven setup, use:
178
178
  - `autotouch cli-manifest --output json` for the local machine-readable command contract
179
179
  - `autotouch cli-reference` for the shipped parser-generated reference
180
180
  - `autotouch capabilities --output json` for provider/workflow contracts
181
- - `autotouch --version` should be `0.2.88` or newer for structured company list builds without user-supplied keywords, stored list-build input lookup, research-workspace list-build guidance, the cleaned single LinkedIn-sourced list-build path, Exa Company Search up to 100 results, and scheduled agent `--target ACCOUNTS|LEADS`
181
+ - `autotouch --version` should be `0.2.90` or newer for structured company list builds without user-supplied keywords, stored list-build input lookup, research-workspace list-build guidance, the cleaned single LinkedIn-sourced list-build path, Exa Company Search up to 100 results, scheduled agent `--target ACCOUNTS|LEADS`, paced HTTP Request column contracts, and V2 sequence flowGraph recipes
182
182
  - `autotouch capabilities --output json --select list_builds` for documented list-build inputs such as geography IDs, company size buckets, profile language, and company IDs
183
183
  - `autotouch list-build inputs` and `autotouch list-build pricing` before creating durable list-build jobs
184
184
  - `autotouch list-build companies` and `autotouch list-build leads` for durable LinkedIn-sourced company and lead list builds with Smart Table-owned background workers, visible progress, and no user-owned LinkedIn connection requirement
@@ -1089,12 +1089,20 @@ _PROVIDER_CONTRACTS: Tuple[ResearchTableProviderContract, ...] = (
1089
1089
  "headers": {},
1090
1090
  "body": None,
1091
1091
  "timeoutSeconds": 30,
1092
+ "rateLimit": {
1093
+ "enabled": False,
1094
+ "minDelayMs": 5000,
1095
+ "jitterMs": 1000,
1096
+ "maxConcurrency": 1,
1097
+ "scope": "credential",
1098
+ },
1092
1099
  },
1093
1100
  },
1094
1101
  recipe_notes=(
1095
1102
  "Use {{column_key}} placeholders in config.url, config.headers, and config.body to reference row values.",
1096
1103
  "Use {{secrets.name}} to resolve a workspace secret by name.",
1097
1104
  "body can be null, a string template, or an object whose values are templated and sent as JSON.",
1105
+ "Set config.rateLimit.enabled=true for paced background execution. The current safe mode is maxConcurrency=1 with minDelayMs/jitterMs spacing, coordinated across workers by organization, host, and scope.",
1098
1106
  "The stored cell value is the parsed response body (JSON or text), not the full transport envelope.",
1099
1107
  "Preview the resolved request with `autotouch columns test-http-request --table-id <TABLE_ID> --data-file column.json` before creating or running the column.",
1100
1108
  ),
@@ -1123,6 +1131,15 @@ _PROVIDER_CONTRACTS: Tuple[ResearchTableProviderContract, ...] = (
1123
1131
  "headers_field": "config.headers",
1124
1132
  "body_field": "config.body",
1125
1133
  "timeout_field": "config.timeoutSeconds",
1134
+ "rate_limit": {
1135
+ "field": "config.rateLimit",
1136
+ "enabled": "optional boolean",
1137
+ "minDelayMs": "optional integer milliseconds between worker requests",
1138
+ "jitterMs": "optional integer random extra delay in milliseconds",
1139
+ "maxConcurrency": "currently only 1 is supported for strict serialized pacing",
1140
+ "scope": "credential | host | column",
1141
+ "credential_scope": "organization + resolved host + referenced {{secrets.name}} names; falls back to column id when no secrets are referenced",
1142
+ },
1126
1143
  "supported_methods": list(_HTTP_REQUEST_METHODS),
1127
1144
  "template_syntax": {
1128
1145
  "row_values": "{{column_key}}",
@@ -1564,6 +1581,34 @@ def validate_column_provider_contract(
1564
1581
  "http_request config.body must be null, a string, or an object.",
1565
1582
  hint="Use a string body template or a JSON object whose values contain {{column_key}} placeholders.",
1566
1583
  )
1584
+ rate_limit = cfg.get("rateLimit") or cfg.get("rate_limit")
1585
+ if rate_limit is not None:
1586
+ if not isinstance(rate_limit, dict):
1587
+ raise ProviderContractValidationError(
1588
+ "invalid_field",
1589
+ "http_request config.rateLimit must be an object when provided.",
1590
+ hint="Use {\"enabled\": true, \"minDelayMs\": 5000, \"jitterMs\": 1000, \"maxConcurrency\": 1, \"scope\": \"credential\"}.",
1591
+ )
1592
+ max_concurrency = rate_limit.get("maxConcurrency", rate_limit.get("max_concurrency", 1))
1593
+ try:
1594
+ parsed_max_concurrency = int(float(max_concurrency))
1595
+ except (TypeError, ValueError):
1596
+ parsed_max_concurrency = 1
1597
+ if parsed_max_concurrency != 1:
1598
+ raise ProviderContractValidationError(
1599
+ "invalid_field",
1600
+ "http_request config.rateLimit.maxConcurrency currently supports only 1.",
1601
+ hint="Use maxConcurrency: 1 for serialized paced requests.",
1602
+ detail={"maxConcurrency": max_concurrency},
1603
+ )
1604
+ scope = str(rate_limit.get("scope") or "credential").strip().lower()
1605
+ if scope not in {"credential", "host", "column"}:
1606
+ raise ProviderContractValidationError(
1607
+ "invalid_field",
1608
+ "http_request config.rateLimit.scope must be credential, host, or column.",
1609
+ hint="Use scope: \"credential\" for API keys, cookies, and session-backed providers.",
1610
+ detail={"scope": rate_limit.get("scope")},
1611
+ )
1567
1612
  return contract
1568
1613
 
1569
1614
  return contract
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "autotouch-cli"
7
- version = "0.2.88"
7
+ version = "0.2.90"
8
8
  description = "Autotouch Smart Table CLI"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
File without changes