@datarails-ai/dr-cli 1.6.151 → 1.6.153

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": 1,
3
- "cliVersion": "1.6.151",
3
+ "cliVersion": "1.6.153",
4
4
  "_contract": "Resolve a command path through `aliases` (canonical prefix + alias segment -> canonical path) before any lookup. A path listed in `pendingCommands`, or whose first segment is a key of `pending`, is UNDECIDED, not unrestricted: fail closed or skip it, and never read a path's absence from `commands` as 'no requirement'. An entry carrying `denied` is UNAVAILABLE to any AGENT-SCOPED credential — one whose `scopes` claim carries at least one `resource:action` entry; a claim that is absent, empty, capability-only or wholly unparseable is UNRESTRICTED, and this mark does not apply to it — whatever else that entry says: it is not a ceiling and it is not always-available, its `denied.reason` says why and `denied.until` names the ticket that lifts it, and the absence of `requires` on it must NEVER be read as 'no requirement'. Only an entry in `commands` with an empty `requires` means always-available.",
5
5
  "commands": {
6
6
  "login": {
@@ -4761,6 +4761,209 @@
4761
4761
  "action": "write"
4762
4762
  }
4763
4763
  ]
4764
+ },
4765
+ "agents list": {
4766
+ "requires": [
4767
+ {
4768
+ "resource": "managed-agents",
4769
+ "action": "read"
4770
+ }
4771
+ ]
4772
+ },
4773
+ "agents get": {
4774
+ "requires": [
4775
+ {
4776
+ "resource": "managed-agents",
4777
+ "action": "read"
4778
+ }
4779
+ ]
4780
+ },
4781
+ "agents versions": {
4782
+ "requires": [
4783
+ {
4784
+ "resource": "managed-agents",
4785
+ "action": "read"
4786
+ }
4787
+ ]
4788
+ },
4789
+ "agents test-link": {
4790
+ "requires": []
4791
+ },
4792
+ "agents refresh": {
4793
+ "requires": [
4794
+ {
4795
+ "resource": "managed-agents",
4796
+ "action": "write"
4797
+ }
4798
+ ]
4799
+ },
4800
+ "agents pull": {
4801
+ "requires": [
4802
+ {
4803
+ "resource": "managed-agents",
4804
+ "action": "read"
4805
+ }
4806
+ ]
4807
+ },
4808
+ "agents update": {
4809
+ "requires": [
4810
+ {
4811
+ "resource": "managed-agents",
4812
+ "action": "write"
4813
+ }
4814
+ ]
4815
+ },
4816
+ "agents create": {
4817
+ "requires": [
4818
+ {
4819
+ "resource": "managed-agents",
4820
+ "action": "write"
4821
+ }
4822
+ ]
4823
+ },
4824
+ "agents mcp-servers": {
4825
+ "requires": [
4826
+ {
4827
+ "resource": "managed-agents",
4828
+ "action": "read"
4829
+ }
4830
+ ]
4831
+ },
4832
+ "agents cli-scopes": {
4833
+ "requires": [
4834
+ {
4835
+ "resource": "managed-agents",
4836
+ "action": "read"
4837
+ }
4838
+ ]
4839
+ },
4840
+ "agents scope-registry": {
4841
+ "requires": [
4842
+ {
4843
+ "resource": "managed-agents",
4844
+ "action": "read"
4845
+ }
4846
+ ]
4847
+ },
4848
+ "agents set-listed": {
4849
+ "requires": [
4850
+ {
4851
+ "resource": "managed-agents",
4852
+ "action": "destructive"
4853
+ }
4854
+ ]
4855
+ },
4856
+ "agents set-global": {
4857
+ "requires": [
4858
+ {
4859
+ "resource": "managed-agents",
4860
+ "action": "destructive"
4861
+ }
4862
+ ]
4863
+ },
4864
+ "agents set-visibility": {
4865
+ "requires": [
4866
+ {
4867
+ "resource": "managed-agents",
4868
+ "action": "destructive"
4869
+ }
4870
+ ]
4871
+ },
4872
+ "agents archive": {
4873
+ "requires": [
4874
+ {
4875
+ "resource": "managed-agents",
4876
+ "action": "destructive"
4877
+ }
4878
+ ]
4879
+ },
4880
+ "agents catalog": {
4881
+ "requires": [
4882
+ {
4883
+ "resource": "managed-agents",
4884
+ "action": "read"
4885
+ }
4886
+ ]
4887
+ },
4888
+ "agents models": {
4889
+ "requires": [
4890
+ {
4891
+ "resource": "managed-agents",
4892
+ "action": "read"
4893
+ }
4894
+ ]
4895
+ },
4896
+ "agents system-entities": {
4897
+ "requires": [
4898
+ {
4899
+ "resource": "managed-agents",
4900
+ "action": "read"
4901
+ }
4902
+ ]
4903
+ },
4904
+ "agents skills list": {
4905
+ "requires": [
4906
+ {
4907
+ "resource": "managed-agents",
4908
+ "action": "read"
4909
+ }
4910
+ ]
4911
+ },
4912
+ "agents skills get": {
4913
+ "requires": [
4914
+ {
4915
+ "resource": "managed-agents",
4916
+ "action": "read"
4917
+ }
4918
+ ]
4919
+ },
4920
+ "agents skills versions": {
4921
+ "requires": [
4922
+ {
4923
+ "resource": "managed-agents",
4924
+ "action": "read"
4925
+ }
4926
+ ]
4927
+ },
4928
+ "agents skills bundle": {
4929
+ "requires": [
4930
+ {
4931
+ "resource": "managed-agents",
4932
+ "action": "read"
4933
+ }
4934
+ ]
4935
+ },
4936
+ "agents skills pull": {
4937
+ "requires": [
4938
+ {
4939
+ "resource": "managed-agents",
4940
+ "action": "read"
4941
+ }
4942
+ ]
4943
+ },
4944
+ "agents skills create": {
4945
+ "requires": [
4946
+ {
4947
+ "resource": "managed-agents",
4948
+ "action": "write"
4949
+ }
4950
+ ]
4951
+ },
4952
+ "agents skills publish": {
4953
+ "requires": [
4954
+ {
4955
+ "resource": "managed-agents",
4956
+ "action": "write"
4957
+ }
4958
+ ]
4959
+ },
4960
+ "agents skills delete": {
4961
+ "requires": [
4962
+ {
4963
+ "resource": "managed-agents",
4964
+ "action": "destructive"
4965
+ }
4966
+ ]
4764
4967
  }
4765
4968
  },
4766
4969
  "aliases": {
@@ -51,13 +51,19 @@ Reads never need confirmation. Gated **writes** fall into two tiers (full table:
51
51
  Still relay the plan and get the user's in-chat "yes" before the execute step,
52
52
  but it will not pop a dialog.
53
53
  - **Non-prod (`DEV`, `DEV-1`, `TEST`, `TEST-1`…`TEST-7`):** a plain `yes` is fine
54
- for either tier — token only.
54
+ for either tier — token only. Exception: `agents set-listed` / `set-global` /
55
+ `set-visibility` / `archive` and `agents skills delete` take the word gate on
56
+ every server, because the agent and skill registry is shared by every
57
+ environment (a write on DEV-1 changes production agents too).
55
58
 
56
59
  **Tier A is the smaller set:** every `delete` (across all resources), plus
57
60
  `workflow`/`mec workflow` `archive` (and its `delete` alias), `process delete`,
58
61
  `step delete`, `task delete`; `forecast scenarios bulk-edit`; `cash rules
59
62
  delete-all`; `filebox archive` and `filebox data-query clear`;
60
63
  `multi-product org-products set`;
64
+ `agents archive` and the three agent-reach flips `agents set-listed` /
65
+ `set-global` / `set-visibility` (each changes who can reach the agent for every
66
+ organization at once);
61
67
  `error-enrichments import` (replaces every rule on the server, zeroes all
62
68
  hits/feedback counters, and reassigns every rule id); and
63
69
  `dashboards push --override` / `filebox push --override`. `import` is **not** an
@@ -81,7 +87,8 @@ with the same alias and tags, the push adds a version to *that* filebox and it
81
87
  becomes the one served. The CLI cannot tell in advance and no word gate will
82
88
  stop it, so check the destination afterwards.
83
89
  Everything else gated is Tier B (all `create`/`update`/`reorder`/`set-*`/`lock`/
84
- `unlock` verbs, `templates set-field-type`, `rescan table` (a full historical
90
+ `unlock` verbs other than the agent-reach flips above,
91
+ `templates set-field-type`, `rescan table` (a full historical
85
92
  rescan — expensive/high-blast but reversible, so token-only; the plan step shows
86
93
  the document + version counts), `filebox rescan-version` (the same, for one
87
94
  version: re-extracts it in place and replaces its rows, never deletes; the plan
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: schedule-drip-task
3
- description: Use when a customer needs files or data pushed OUT of Datarails on a schedule through DRIP (the connectors platform) - an SFTP export, "send this CSV to the customer's SFTP every hour", "push the file to their server", "schedule an export", "scheduled task", "dr connectors tasks", "sftp_export", "lib/sftp_export", or when operating an existing schedule - "is the export running", "why did the export fail", "the task is paused", "run the export now", "pause the export", "task runs", "consecutive failures", "paused (failures)", "paused (schedule)", "no active version", "newest source file is older than max_age_seconds", "the target must not be a customer org-*-sftp container", a 403 "scheduled tasks need a support user or an org admin". Not for pulling data INTO Datarails (drip-implement-connector) or for polled SFTP ingestion (dr connectors poll).
3
+ description: Use when a customer needs files or data pushed OUT of Datarails on a schedule through DRIP (the connectors platform) - an SFTP export, "send this CSV to the customer's SFTP every hour", "push the file to their server", "schedule an export", "scheduled task", "dr connectors tasks", "sftp_export", "lib/sftp_export", or when operating an existing schedule - "is the export running", "why did the export fail", "the task is paused", "run the export now", "pause the export", "task runs", "consecutive failures", "paused (failures)", "paused (schedule)", "no active version", "newest source file is older than max_age_seconds", "the target must not be a customer org-*-sftp container", a 403 "scheduled tasks need a support user or an org admin", or when writing a new task type - "write a scheduled task", "new DRIP task", "process() TaskResult", "run hook", "task state". Not for pulling data INTO Datarails (drip-implement-connector) or for polled SFTP ingestion (dr connectors poll).
4
4
  ---
5
5
 
6
6
  # Schedule a DRIP task
@@ -10,6 +10,9 @@ datasources, and keeps a run history. The control plane only schedules; the runn
10
10
  artifact's `process()`. The first task type is **SFTP Export** (`lib/sftp_export`): copy files from
11
11
  our hosted SFTP to a customer's SFTP under a name the customer wants.
12
12
 
13
+ To write a task type other than SFTP Export (the `process()` hook, `TaskResult`, state, retries),
14
+ see [reference/writing-a-task.md](reference/writing-a-task.md).
15
+
13
16
  Commands: `dr connectors tasks create | list | get | update | delete | run | pause | resume | runs`.
14
17
  Every one takes `--server`, `--account` and `--json`; a `<task>` is its id or its name.
15
18
 
@@ -16,10 +16,11 @@ datasources (the control plane refuses secret-looking keys with a 422).
16
16
  | `settle_seconds` | no | 120 | Skip files modified more recently (the producer may still be writing). 0 to 7 days |
17
17
  | `max_age_seconds` | no | none | `newest` only: fail the run if the newest file is older than this before the scheduled time, or if no file matches (producer late or stopped) |
18
18
  | `max_files_per_run` | no | 50 | `all_new`: at most this many files per run (1 to 1000); the rest go next run |
19
+ | `upload` | no | `atomic` | `atomic`: write a `.part`, then rename it, so no reader sees a half-written file. `direct`: write the final name itself, for a target folder that takes files away as they arrive (the `.part` would be gone before the rename). `newest` only |
19
20
 
20
21
  Behaviour worth knowing:
21
22
 
22
- - Upload is atomic: a random `.<name>.<hex>.part` is written, then renamed. The customer's login must
23
+ - Upload is atomic by default: a random `.<name>.<hex>.part` is written, then renamed. A run that fails with "a file vanished while publishing the upload" means the target takes files as they arrive: set `"upload": "direct"`. The customer's login must
23
24
  be allowed to create and rename files in `target_dir`. Stale parts of ours older than 2 h are swept.
24
25
  - The source is never written, moved or deleted. Cleaning it is the producer's job; with `all_new`,
25
26
  a folder that keeps every sent file eventually fails the run with "archive or delete them".
@@ -0,0 +1,92 @@
1
+ # Writing a new task type
2
+
3
+ A scheduled task is an ordinary DRIP artifact in dr-connectors (a connection plus a dataquery,
4
+ published and made active like any connector, see `drip-implement-connector`), whose dataquery
5
+ implements `process()`. The control plane only schedules it. On each run it sends the flavor's
6
+ runner a job with `hook: "run"`, and the runner calls `connect()`, then `process()`, in one job.
7
+ The full contract is in dr-connectors-runner `docs/connector-kit.md`, "Scheduled tasks: the `run`
8
+ hook". `lib/sftp_export` in dr-connectors is the reference implementation.
9
+
10
+ ## The code
11
+
12
+ ```python
13
+ # lib/<task>/dataqueries/<name>.py
14
+ from dr_integrations_sdk import DataQuery, PermanentError, SyncRequest, TaskResult, kit
15
+
16
+ OBJECT_TYPES = ("<objectType>",)
17
+
18
+
19
+ class MyTask(DataQuery):
20
+ async def process(self, session, req: SyncRequest) -> TaskResult:
21
+ config = req.config # the task's --config, validated here
22
+ state = (req.metadata or {}).get("state") or {} # what the last ok run returned
23
+ scheduled_for = req.metadata["scheduled_for"] # ISO UTC time of the slot this run is for
24
+ other = await kit.auth.get_credentials_for(req, "source") # a non-primary datasource, by role
25
+ sent = await do_the_work(session, other, config, state)
26
+ return TaskResult(state={"sent": sent}, summary={"sent": len(sent)})
27
+
28
+
29
+ def get_query_class(object_type: str):
30
+ if object_type == "<objectType>":
31
+ return MyTask
32
+ raise PermanentError(f"unknown objectType {object_type!r}")
33
+ ```
34
+
35
+ | Piece | Rule |
36
+ |---|---|
37
+ | `connect()` | Gets the **primary** datasource's credentials as for any connector. Log in here, so a wrong password or host key fails before any work |
38
+ | `req.config` | The task's `--config` JSON. Must carry `objectType` (it picks the dataquery class). Validate it before any login. Never put credentials in it: the control plane refuses secret-looking keys with a 422 |
39
+ | Other datasources | `kit.auth.get_credentials_for(req, "<role>")`, one per `--datasource <role>=<id>`. An unbound role is a `PermanentError` naming it |
40
+ | `req.metadata` | `state`, `scheduled_for`, `run_id`, `attempt` (1, or 2 on the retry), `trigger` (`schedule` or `manual`) |
41
+ | Imports | stdlib, `dr_integrations_sdk`, `lib` and the flavor's allowlist only (dr-connector-images). Set `RUNTIME_FLAVOR` in the connection |
42
+ | Logs | `kit.logging.get_logger("<task>")`. They reach Groundcover under the runner flavor's workload |
43
+
44
+ ## The result
45
+
46
+ `TaskResult(state=…, summary=…, verdict=…)`. Every field is optional.
47
+
48
+ - **`state`**: the task's own bookkeeping (ids, cursors, names already sent), never the data
49
+ processed. It must be a JSON object or `None`, at most 64 KiB. The control plane keeps it only
50
+ from an `ok` run; after a failure the next run gets the last good state.
51
+ - **`summary`**: what the run did, shown by `dr connectors tasks runs`.
52
+ - **`verdict`**: `ok` (default), `retry` (the run is sent once more after 60 s) or `block` (the run
53
+ fails).
54
+ - The whole result must be JSON (no `datetime`, `set`, `Decimal`, NaN) and at most 128 KiB.
55
+ Anything else fails the run as a connector error.
56
+
57
+ ## Errors
58
+
59
+ | Raised | Run |
60
+ |---|---|
61
+ | `PermanentError`, `AuthError` | Fails, no retry. Use for bad config, refused credentials, missing folders |
62
+ | `TransientError`, `httpx` 429/5xx, `httpx.TransportError` | Retried once after 60 s, then fails |
63
+ | Anything else | Fails as a connector error |
64
+
65
+ - After 5 failed runs in a row the task pauses itself (`paused (failures)`).
66
+ - The control plane stores only the first few hundred characters of a run's error. Keep the message
67
+ short. Log anything longer, such as a key or a listing, and read it in Groundcover.
68
+ - Never put a secret in an error or a log line.
69
+
70
+ ## Make it safe to run twice
71
+
72
+ A retry runs `process()` again, a manual run can land next to a scheduled one, and a run that dies
73
+ leaves no state. So:
74
+
75
+ - Record what was done in `state`, and skip it next time.
76
+ - Write the output so that a half-finished run leaves nothing a reader would take as done. For
77
+ example, upload to a temporary name and rename it.
78
+ - Use `scheduled_for`, not the clock, for anything named after the run's time: a retry then makes
79
+ the same name.
80
+
81
+ ## Ship it
82
+
83
+ 1. **Shared code goes in `lib/<task>/`**, with tests. A thin per-org artifact re-exports it. The
84
+ dataquery must set its own `CONNECTION` (the thin connection file's name), as in
85
+ [SKILL.md](../SKILL.md) step 3.
86
+ 2. **Merge to dr-connectors.** CI publishes the artifact to JFrog.
87
+ 3. **Register and activate it on the region:** `dr connectors publish <dataquery file>`, then
88
+ `dr connectors promote <artifact> --artifact-version v<N>`, as the customer's support user.
89
+ 4. **Schedule it:** `dr connectors tasks create` (see [SKILL.md](../SKILL.md)), then `tasks run` once
90
+ and check `tasks runs`.
91
+ 5. **Test on dev-1 first.** The heartbeat artifact in dr-connectors
92
+ (`dev-1/69_(test-connectors.com)/heartbeat/`) is a minimal task that carries a counter in `state`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datarails-ai/dr-cli",
3
- "version": "1.6.151",
3
+ "version": "1.6.153",
4
4
  "description": "Datarails CLI",
5
5
  "type": "module",
6
6
  "engines": {