@nsp-labs/agnostic-sdk 1.0.4 → 1.4.0

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.
package/CHANGELOG.md CHANGED
@@ -9,6 +9,73 @@ and pass the runtime OpenAPI schema check.
9
9
 
10
10
  ## Unreleased
11
11
 
12
+ ## 1.4.0 - 2026-09-02
13
+
14
+ ### Added
15
+
16
+ - Added `data.table(name).records.aggregate(...)` for bounded server-side
17
+ `count`, `sum`, `avg`, `min`, and `max` queries with filters, search, up to
18
+ two grouping dimensions, ordering, and a capped result size.
19
+
20
+ ### Changed
21
+
22
+ - Regenerated the Runtime OpenAPI client with the backwards-compatible Data
23
+ aggregate endpoint. The runtime identity remains the only source of project
24
+ and environment scope.
25
+
26
+ ## 1.3.0 - 2026-08-12
27
+
28
+ ### Added
29
+
30
+ - Added `storage.bucket(name).createUpload`, `completeUpload`, `get`,
31
+ `createDownloadUrl`, and `delete` with project/environment/runtime-owner
32
+ binding and stable Files errors.
33
+ - Added the canonical `files.read`, `files.write`, `files.delete`, and
34
+ `files.links.create` Runtime API capability mapping.
35
+
36
+ ### Changed
37
+
38
+ - Regenerated the Runtime OpenAPI client for the complete Files facade while
39
+ keeping provider bucket names, object keys, and credentials outside public
40
+ types.
41
+ - Bumped the compatible server-only SDK contract to `1.3.0`; existing runtime
42
+ paths remain backwards compatible.
43
+
44
+ ## 1.2.0 - 2026-08-10
45
+
46
+ ### Added
47
+
48
+ - Added the server-only
49
+ `storage.bucket(name).anonymousUploads.claim(...)` helper and its Runtime API
50
+ schema for idempotent anonymous session claim with ready/processing handles.
51
+
52
+ ### Changed
53
+
54
+ - Clarified in the exported Runtime SDK manifest that App Auth self-service
55
+ profile mutations use the public App Auth API through the project backend/BFF;
56
+ they are not runtime-token mutations and do not expand the callable SDK
57
+ surface.
58
+ - Generated the runtime OpenAPI layer with the backwards-compatible Files claim
59
+ endpoint; existing Runtime API paths are unchanged.
60
+
61
+ ## 1.1.0 - 2026-07-26
62
+
63
+ ### Added
64
+
65
+ - Added an exact public-surface drift guard and a deterministic manifest
66
+ fingerprint so Agent codegen can re-read the installed SDK contract on every
67
+ generation step instead of relying on a copied namespace list.
68
+ - Added `auth.getSession()` for optional App Auth sessions and
69
+ `auth.requireScopes()` for application-level scope guardrails after session
70
+ verification.
71
+
72
+ ### Changed
73
+
74
+ - Runtime SDK manifest consumers can now derive scaffold versions, Agent review
75
+ allowlists, and source-write deny rules from the exported package contract.
76
+ The runtime OpenAPI schema is unchanged; the callable SDK surface adds only
77
+ the backwards-compatible App Auth helpers listed above.
78
+
12
79
  ## 1.0.4 - 2026-06-18
13
80
 
14
81
  ### Changed
package/README.md CHANGED
@@ -36,7 +36,8 @@ Runtime tokens are server-side credentials. Do not put
36
36
  `AGNOSTIC_RUNTIME_TOKEN` in browser, mobile, Vite, or static frontend bundles.
37
37
 
38
38
  The current public runtime surface includes `auth`, `context`, `data`,
39
- `workflows`, and `workflowRuns`.
39
+ `storage`, `workflows`, and `workflowRuns`. `storage.bucket(name)` supports the
40
+ server-side Files lifecycle and anonymous upload claim.
40
41
 
41
42
  ## App Auth
42
43
 
@@ -46,13 +47,33 @@ the incoming App Auth session through the runtime helper:
46
47
  ```ts
47
48
  const session = await agnostic.auth.requireSession(request);
48
49
 
49
- await agnostic.data.table('orders').records.update(
50
- 'order_123',
51
- { values: { status: 'fulfilled' } },
52
- { actor: session.actor },
53
- );
50
+ await agnostic.data
51
+ .table('orders')
52
+ .records.update(
53
+ 'order_123',
54
+ { values: { status: 'fulfilled' } },
55
+ { actor: session.actor },
56
+ );
57
+ ```
58
+
59
+ Use `getSession(request)` for routes that support both guests and signed-in
60
+ users. It returns `null` when the request has no App Auth session:
61
+
62
+ ```ts
63
+ const session = await agnostic.auth.getSession(request);
54
64
  ```
55
65
 
66
+ Use `requireScopes(request, scopes)` for application-level scope guardrails.
67
+ This verifies the session first and throws `app_auth_scope_missing` when one of
68
+ the required app-user scopes is absent:
69
+
70
+ ```ts
71
+ const session = await agnostic.auth.requireScopes(request, ['orders:write']);
72
+ ```
73
+
74
+ Scope checks do not replace ownership or other business rules in the
75
+ application backend.
76
+
56
77
  The helper extracts `Authorization: Bearer <app-session>` or an
57
78
  `agnostic_app_session_*` cookie, then calls
58
79
  `/api/v1/runtime/app-auth/session/verify` with the server-side runtime token.
@@ -60,6 +81,15 @@ It returns sanitized user/session claims and an `app_user` actor for audit
60
81
  metadata. `agnostic.auth.getUser(userId)` reads sanitized App Auth user claims
61
82
  for the same runtime project.
62
83
 
84
+ For a browser frontend and backend deployed on different hosts, login must use
85
+ the application backend/BFF. The BFF requests bearer mode from App Auth and
86
+ places the tokens in host-only HttpOnly cookies on its own host before calling
87
+ `requireSession(request)`. A host-only cookie set by `api.agn0.ru` is not sent
88
+ to an application backend host. Do not solve this with `Domain=.agn0.ru` or
89
+ browser token storage. For cross-origin frontend/backend pairs, return a
90
+ separate non-secret CSRF token from the BFF and validate it on state-changing
91
+ cookie-authenticated requests.
92
+
63
93
  ## Data
64
94
 
65
95
  ```ts
@@ -68,6 +98,17 @@ const orders = await agnostic.data.table('orders').records.list({
68
98
  limit: 50,
69
99
  });
70
100
 
101
+ const revenueByStatus = await agnostic.data.table('orders').records.aggregate({
102
+ filter: { currency: 'RUB' },
103
+ groupBy: ['status'],
104
+ metrics: [
105
+ { operation: 'count', as: 'orders' },
106
+ { operation: 'sum', field: 'amount', as: 'revenue' },
107
+ ],
108
+ orderBy: [{ by: 'revenue', direction: 'desc' }],
109
+ limit: 20,
110
+ });
111
+
71
112
  const created = await agnostic.data.table('orders').records.create({
72
113
  values: {
73
114
  customerId: 'cust_123',
@@ -82,6 +123,10 @@ await agnostic.data.table('orders').records.update(created.id, {
82
123
  await agnostic.data.table('orders').records.delete(created.id);
83
124
  ```
84
125
 
126
+ Aggregate queries support `count`, `sum`, `avg`, `min`, and `max`. They are
127
+ executed by the data provider, allow at most two grouping fields, return at
128
+ most 100 groups, and require the existing `data.read` capability.
129
+
85
130
  ## Workflows
86
131
 
87
132
  ```ts
@@ -94,6 +139,55 @@ const finished = await agnostic.workflowRuns.wait(run.id, {
94
139
  });
95
140
  ```
96
141
 
142
+ ## Anonymous File Claim
143
+
144
+ After creating the business object, claim the anonymous upload session from the
145
+ application backend. Reuse the same `externalRef` and `idempotencyKey` for saga
146
+ retries:
147
+
148
+ ```ts
149
+ const attachments = await agnostic.storage
150
+ .bucket('order-files')
151
+ .anonymousUploads.claim({
152
+ token: attachmentSessionToken,
153
+ externalRef: `order:${order.id}`,
154
+ idempotencyKey: `order:${order.id}`,
155
+ });
156
+ ```
157
+
158
+ The runtime token and anonymous claim token must remain server-side. The result
159
+ contains server-selected `ready` or `processing` file handles; callers do not
160
+ submit file IDs.
161
+
162
+ ## Files
163
+
164
+ ```ts
165
+ const bucket = agnostic.storage.bucket('uploads');
166
+ const upload = await bucket.createUpload({
167
+ fileName: 'invoice.pdf',
168
+ contentType: 'application/pdf',
169
+ size: contents.byteLength,
170
+ checksumSha256,
171
+ access: 'private',
172
+ idempotencyKey: `invoice:${invoice.id}:v1`,
173
+ });
174
+
175
+ await fetch(upload.uploadUrl, {
176
+ method: 'PUT',
177
+ headers: upload.requiredHeaders,
178
+ body: contents,
179
+ });
180
+
181
+ const completed = await bucket.completeUpload(upload.id);
182
+ const file = await bucket.get(completed.file.id);
183
+ const download = await bucket.createDownloadUrl(file.id);
184
+ await bucket.delete(file.id);
185
+ ```
186
+
187
+ Reuse the same idempotency key and IDs when retrying. Keep the SDK and runtime
188
+ token server-side; only a short scoped upload/download URL may cross into a
189
+ browser after the application has authorized the request.
190
+
97
191
  ## Actor Metadata
98
192
 
99
193
  After an application backend has verified its App Auth session and business
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nsp-labs/agnostic-sdk",
3
- "version": "1.0.4",
3
+ "version": "1.4.0",
4
4
  "description": "Server-side Runtime SDK for Agnostic project services, workers, automations, and trusted local scripts.",
5
5
  "license": "ISC",
6
6
  "type": "commonjs",