current-cli 0.1.35__tar.gz → 0.1.37__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: current-cli
3
- Version: 0.1.35
3
+ Version: 0.1.37
4
4
  Summary: Command line interface for the Current API, generated from its OpenAPI schema at runtime
5
5
  Author-email: Orin <your-email@example.com>
6
6
  License-Expression: MIT
@@ -16,9 +16,14 @@ def _make_callback(op: Operation):
16
16
  for param in op.path_params:
17
17
  path = path.replace("{" + param.name + "}", str(kwargs.pop(param.name)))
18
18
 
19
- params: dict[str, str] = {}
19
+ params: dict[str, str | list[str]] = {}
20
20
  for param in op.query_params:
21
21
  value = kwargs.pop(param.name, None)
22
+ # click gives a repeatable option an empty tuple, not None, when
23
+ # it is absent; treat that as "not given" so it never becomes an
24
+ # empty query key.
25
+ if param.is_array:
26
+ value = list(value) if value else None
22
27
  if value is None and param.name == "workspace":
23
28
  value = config.get("default_workspace")
24
29
  if value is None:
@@ -48,6 +53,7 @@ def _make_command(op: Operation) -> click.Command:
48
53
  click.Option(
49
54
  [f"--{param.name.replace('_', '-')}"],
50
55
  required=False,
56
+ multiple=param.is_array,
51
57
  help=param.description or None,
52
58
  )
53
59
  )
@@ -21,6 +21,9 @@ class Parameter:
21
21
  location: str # "path" or "query"
22
22
  required: bool
23
23
  description: str
24
+ # An array query param is sent as the repeated key the API reads with
25
+ # getlist (?fields=a&fields=b), so its option has to be repeatable too.
26
+ is_array: bool = False
24
27
 
25
28
 
26
29
  @dataclass(frozen=True)
@@ -90,6 +93,7 @@ def extract_operations(spec: dict) -> list[Operation]:
90
93
  location=raw["in"],
91
94
  required=raw.get("required", False),
92
95
  description=raw.get("description", ""),
96
+ is_array=raw.get("schema", {}).get("type") == "array",
93
97
  )
94
98
  if param.location == "path":
95
99
  path_params.append(param)
@@ -1053,6 +1053,16 @@ paths:
1053
1053
  definition the agent declares no inputs, so it gets the company tree
1054
1054
  plus every project tree and decides for itself what it needs.
1055
1055
 
1056
+ An index, not a copy: the sandbox fetches a file's bytes only when the
1057
+ model reads that file. That is what lets this stay workspace-wide now
1058
+ that a workspace mirrors its Sitetracker documents — gigabytes of
1059
+ scans that ``read_file`` could only refuse, and that an attachment
1060
+ reads here from object storage rather than from the sandbox.
1061
+
1062
+ ``truncated`` says the workspace holds more than this answer names, so
1063
+ the agent can tell a user "I cannot see all of your files" instead of
1064
+ reporting a file it was never given as one the workspace does not have.
1065
+
1056
1066
  POST, for a read, because the job-token permission class reads the
1057
1067
  workspace id out of the request body — the same contract every other
1058
1068
  messaging-job endpoint uses.
@@ -1841,12 +1851,17 @@ paths:
1841
1851
  get:
1842
1852
  operationId: projects_projects_inventory_retrieve
1843
1853
  description: |-
1844
- Page current field values, optionally narrowed to named keys.
1854
+ Page current field values, optionally narrowed to named keys and
1855
+ to projects whose values match.
1845
1856
 
1846
1857
  The full project list serializes every field of every project —
1847
1858
  megabytes at production scale. This page is the compact census a
1848
1859
  chat agent can scan: identity plus the keys it asked for, no
1849
- timeline, no provenance. See state.list_projects.
1860
+ timeline, no provenance.
1861
+
1862
+ Filter here rather than page-and-sift: a caller that reads every
1863
+ page and picks the matches by eye miscounts, and "total" under a
1864
+ filter is the count it was really after. See state.list_projects.
1850
1865
  parameters:
1851
1866
  - in: query
1852
1867
  name: fields
@@ -1855,6 +1870,16 @@ paths:
1855
1870
  items:
1856
1871
  type: string
1857
1872
  description: Field keys to keep; omit to return every current value
1873
+ - in: query
1874
+ name: filter
1875
+ schema:
1876
+ type: array
1877
+ items:
1878
+ type: string
1879
+ description: Keep only projects whose current value matches, as 'key:value'
1880
+ (for example 'status:Active'). Repeat for more fields; repeating one key
1881
+ accepts any of its values. An option matches by label, value, or ID. 'total'
1882
+ counts the matching projects.
1858
1883
  - in: query
1859
1884
  name: limit
1860
1885
  schema:
@@ -1865,6 +1890,14 @@ paths:
1865
1890
  schema:
1866
1891
  type: integer
1867
1892
  description: Number of projects to skip (default 0)
1893
+ - in: query
1894
+ name: values
1895
+ schema:
1896
+ type: string
1897
+ enum:
1898
+ - ids
1899
+ - labels
1900
+ description: Read option values as labels (default) or as option IDs
1868
1901
  - in: query
1869
1902
  name: workspace
1870
1903
  schema:
@@ -4218,6 +4251,9 @@ components:
4218
4251
  type: array
4219
4252
  items:
4220
4253
  $ref: '#/components/schemas/ManifestEntry'
4254
+ truncated:
4255
+ type: boolean
4256
+ default: false
4221
4257
  required:
4222
4258
  - files
4223
4259
  Me:
@@ -383,6 +383,16 @@ message can turn out to be about any project, and unlike a workflow
383
383
  definition the agent declares no inputs, so it gets the company tree
384
384
  plus every project tree and decides for itself what it needs.
385
385
 
386
+ An index, not a copy: the sandbox fetches a file's bytes only when the
387
+ model reads that file. That is what lets this stay workspace-wide now
388
+ that a workspace mirrors its Sitetracker documents — gigabytes of
389
+ scans that ``read_file`` could only refuse, and that an attachment
390
+ reads here from object storage rather than from the sandbox.
391
+
392
+ ``truncated`` says the workspace holds more than this answer names, so
393
+ the agent can tell a user "I cannot see all of your files" instead of
394
+ reporting a file it was never given as one the workspace does not have.
395
+
386
396
  POST, for a read, because the job-token permission class reads the
387
397
  workspace id out of the request body — the same contract every other
388
398
  messaging-job endpoint uses.
@@ -617,19 +627,26 @@ Options:
617
627
 
618
628
  ### current projects inventory retrieve
619
629
 
620
- Page current field values, optionally narrowed to named keys.
630
+ Page current field values, optionally narrowed to named keys and
631
+ to projects whose values match.
621
632
 
622
633
  The full project list serializes every field of every project —
623
634
  megabytes at production scale. This page is the compact census a
624
635
  chat agent can scan: identity plus the keys it asked for, no
625
- timeline, no provenance. See state.list_projects.
636
+ timeline, no provenance.
637
+
638
+ Filter here rather than page-and-sift: a caller that reads every
639
+ page and picks the matches by eye miscounts, and "total" under a
640
+ filter is the count it was really after. See state.list_projects.
626
641
 
627
642
  GET /api/projects/projects/inventory/
628
643
 
629
644
  Options:
630
645
  - `--fields` - Field keys to keep; omit to return every current value
646
+ - `--filter` - Keep only projects whose current value matches, as 'key:value' (for example 'status:Active'). Repeat for more fields; repeating one key accepts any of its values. An option matches by label, value, or ID. 'total' counts the matching projects.
631
647
  - `--limit` - Max projects per page (default 100, max 200)
632
648
  - `--offset` - Number of projects to skip (default 0)
649
+ - `--values` - Read option values as labels (default) or as option IDs
633
650
  - `--workspace` - Workspace UUID
634
651
 
635
652
  ### current projects list
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "current-cli"
3
- version = "0.1.35"
3
+ version = "0.1.37"
4
4
  description = "Command line interface for the Current API, generated from its OpenAPI schema at runtime"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -60,6 +60,40 @@ def test_create_sends_json_body(cli, runner, transport_factory):
60
60
  assert json.loads(request.content) == {"name": "Order beams", "workspace": "w1"}
61
61
 
62
62
 
63
+ def test_array_query_param_repeats(cli, runner, transport_factory):
64
+ """An array param must repeat its key, the shape the API reads with
65
+ getlist — one AND-ed filter per --filter."""
66
+ transport = transport_factory(ok({}))
67
+
68
+ result = runner.invoke(
69
+ cli,
70
+ [
71
+ "projects",
72
+ "inventory",
73
+ "retrieve",
74
+ "--workspace",
75
+ "ws-uuid",
76
+ "--filter",
77
+ "status:Active",
78
+ "--filter",
79
+ "site_type:Garden Style",
80
+ ],
81
+ )
82
+
83
+ assert result.exit_code == 0, result.output
84
+ params = transport.requests[0].url.params
85
+ assert params.get_list("filter") == ["status:Active", "site_type:Garden Style"]
86
+
87
+
88
+ def test_array_query_param_is_absent_when_not_given(cli, runner, transport_factory):
89
+ transport = transport_factory(ok({}))
90
+
91
+ result = runner.invoke(cli, ["projects", "inventory", "retrieve", "--workspace", "ws-uuid"])
92
+
93
+ assert result.exit_code == 0, result.output
94
+ assert "filter" not in transport.requests[0].url.params
95
+
96
+
63
97
  def test_default_workspace_fallback(cli, runner, transport_factory):
64
98
  config.set_value("default_workspace", "default-ws")
65
99
  transport = transport_factory(ok({}))
File without changes
File without changes