@raidiant/notifai 2.0.2 → 3.0.1
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 +21 -0
- package/dist/client.d.ts +2 -1
- package/dist/client.js +7 -0
- package/dist/client.js.map +1 -1
- package/package.json +2 -2
- package/src/client.test.ts +46 -0
- package/src/client.ts +13 -0
- package/src/commands.test.ts +8 -16
- package/src/hooks.test.ts +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
8
8
|
This project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html)
|
|
9
9
|
and [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/).
|
|
10
10
|
|
|
11
|
+
## [3.0.1](https://github.com/Raidiant-io/notifai/compare/v3.0.0...v3.0.1) (2026-08-14)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Bug Fixes
|
|
15
|
+
|
|
16
|
+
* **cli:** require protocol 0.6.0 ([fc0f956](https://github.com/Raidiant-io/notifai/commit/fc0f9565d5dff4d98829cfc4b468eb272e15ede1))
|
|
17
|
+
* **cli:** require protocol 0.6.0 ([afc60f1](https://github.com/Raidiant-io/notifai/commit/afc60f1b4f825fd12d24cc12a33abb002d8dc978))
|
|
18
|
+
|
|
19
|
+
## [3.0.0](https://github.com/Raidiant-io/notifai/compare/v2.0.2...v3.0.0) (2026-08-14)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
### ⚠ BREAKING CHANGES
|
|
23
|
+
|
|
24
|
+
* **cli:** finalize media uploads before use
|
|
25
|
+
|
|
26
|
+
### Features
|
|
27
|
+
|
|
28
|
+
* **cli:** finalize media uploads before use ([6b6d334](https://github.com/Raidiant-io/notifai/commit/6b6d3344c9e997f0b66fb78274fab87692d9d92b))
|
|
29
|
+
* **protocol:** add billing error statuses ([f7cbca2](https://github.com/Raidiant-io/notifai/commit/f7cbca2663469776d079787fa3b608bcecd40f45))
|
|
30
|
+
* require authoritative media upload finalization ([1150ffe](https://github.com/Raidiant-io/notifai/commit/1150ffe18d004da4f9809be08f3b5875b31d3a21))
|
|
31
|
+
|
|
11
32
|
## [2.0.2](https://github.com/Raidiant-io/notifai/compare/v2.0.1...v2.0.2) (2026-08-14)
|
|
12
33
|
|
|
13
34
|
|
package/dist/client.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AccountAccessResponse, BeginPairingResponse, CapabilityDocument, CreateMediaUploadRequestT, CreateMediaUploadResponse, EvidenceSnapshot, GetAgentAcknowledgementResponse, ListDevicesResponse, ListRepliesResponse, Platform, PollPairingResponse, PutAgentAcknowledgementRequestT, PutAgentAcknowledgementResponse, SubmissionReceipt, SubmitNotificationRequestT } from '@raidiant/notifai-protocol';
|
|
1
|
+
import type { AccountAccessResponse, BeginPairingResponse, CapabilityDocument, CreateMediaUploadRequestT, CreateMediaUploadResponse, FinalizeMediaUploadResponse, EvidenceSnapshot, GetAgentAcknowledgementResponse, ListDevicesResponse, ListRepliesResponse, Platform, PollPairingResponse, PutAgentAcknowledgementRequestT, PutAgentAcknowledgementResponse, SubmissionReceipt, SubmitNotificationRequestT } from '@raidiant/notifai-protocol';
|
|
2
2
|
import type { Logger } from './logging.js';
|
|
3
3
|
export declare class ApiCallError extends Error {
|
|
4
4
|
status: number;
|
|
@@ -39,6 +39,7 @@ export interface ApiClient {
|
|
|
39
39
|
waitSeconds: number;
|
|
40
40
|
}): Promise<GetAgentAcknowledgementResponse>;
|
|
41
41
|
createMediaUpload(body: CreateMediaUploadRequestT): Promise<CreateMediaUploadResponse>;
|
|
42
|
+
finalizeMediaUpload(mediaId: string): Promise<FinalizeMediaUploadResponse>;
|
|
42
43
|
uploadMedia(grant: CreateMediaUploadResponse, bytes: Uint8Array): Promise<void>;
|
|
43
44
|
health(): Promise<boolean>;
|
|
44
45
|
}
|
package/dist/client.js
CHANGED
|
@@ -132,6 +132,9 @@ export function createClient(baseUrl, bearer, options = {}) {
|
|
|
132
132
|
putAgentAcknowledgement: (requestId, body) => call('PUT', `/api/v1/notifications/${encodeURIComponent(requestId)}/agent-acknowledgement`, body),
|
|
133
133
|
agentAcknowledgement: (requestId, { waitSeconds }) => call('GET', `/api/v1/notifications/${encodeURIComponent(requestId)}/agent-acknowledgement?wait_seconds=${waitSeconds}`, undefined, waitSeconds),
|
|
134
134
|
createMediaUpload: (body) => call('POST', '/api/v1/media', body),
|
|
135
|
+
// Storage-observed bytes become authoritative before a send can reference
|
|
136
|
+
// the upload; the caller's declared size is only a request.
|
|
137
|
+
finalizeMediaUpload: (mediaId) => call('POST', `/api/v1/media/${encodeURIComponent(mediaId)}/finalize`),
|
|
135
138
|
uploadMedia: async (grant, bytes) => {
|
|
136
139
|
let response;
|
|
137
140
|
try {
|
|
@@ -150,6 +153,10 @@ export function createClient(baseUrl, bearer, options = {}) {
|
|
|
150
153
|
if (!response.ok) {
|
|
151
154
|
throw new NetworkError(`Upload rejected with status ${response.status}`);
|
|
152
155
|
}
|
|
156
|
+
// A successful storage PUT is not authorization to reference the media.
|
|
157
|
+
// The idempotent finalize call makes the server inspect provider metadata
|
|
158
|
+
// and commit authoritative quota before the command can use media_id.
|
|
159
|
+
await call('POST', `/api/v1/media/${encodeURIComponent(grant.media_id)}/finalize`);
|
|
153
160
|
},
|
|
154
161
|
health: async () => {
|
|
155
162
|
try {
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@raidiant/notifai",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Send native phone notifications from agents and local programs",
|
|
6
6
|
"bin": {
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@clack/prompts": "^1.7.0",
|
|
24
|
-
"@raidiant/notifai-protocol": "0.
|
|
24
|
+
"@raidiant/notifai-protocol": "0.6.0",
|
|
25
25
|
"commander": "^14.0.0",
|
|
26
26
|
"picocolors": "^1.1.1",
|
|
27
27
|
"smol-toml": "^1.4.0"
|
package/src/client.test.ts
CHANGED
|
@@ -133,6 +133,52 @@ describe('a server that never answers', () => {
|
|
|
133
133
|
])
|
|
134
134
|
})
|
|
135
135
|
|
|
136
|
+
it('finalizes provider metadata before treating a storage upload as usable', async () => {
|
|
137
|
+
const seen: { method?: string; url?: string; authorization?: string; bytes: number }[] = []
|
|
138
|
+
const baseUrl = await serving((request, response) => {
|
|
139
|
+
const chunks: Buffer[] = []
|
|
140
|
+
request.on('data', (chunk) => chunks.push(Buffer.from(chunk)))
|
|
141
|
+
request.on('end', () => {
|
|
142
|
+
seen.push({
|
|
143
|
+
method: request.method,
|
|
144
|
+
url: request.url,
|
|
145
|
+
...(request.headers.authorization
|
|
146
|
+
? { authorization: request.headers.authorization }
|
|
147
|
+
: {}),
|
|
148
|
+
bytes: Buffer.concat(chunks).byteLength,
|
|
149
|
+
})
|
|
150
|
+
if (request.method === 'POST') {
|
|
151
|
+
response.setHeader('content-type', 'application/json')
|
|
152
|
+
response.end(JSON.stringify({ media_id: 'med_1', size_bytes: 4, status: 'ready' }))
|
|
153
|
+
} else {
|
|
154
|
+
response.statusCode = 200
|
|
155
|
+
response.end()
|
|
156
|
+
}
|
|
157
|
+
})
|
|
158
|
+
})
|
|
159
|
+
const client = createClient(baseUrl, 'Bearer machine')
|
|
160
|
+
|
|
161
|
+
await client.uploadMedia(
|
|
162
|
+
{
|
|
163
|
+
media_id: 'med_1',
|
|
164
|
+
upload_url: `${baseUrl}/signed-upload`,
|
|
165
|
+
upload_headers: { 'content-type': 'image/png' },
|
|
166
|
+
expires_at: '2026-08-14T12:00:00.000Z',
|
|
167
|
+
},
|
|
168
|
+
new Uint8Array([1, 2, 3, 4]),
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
expect(seen).toEqual([
|
|
172
|
+
{ method: 'PUT', url: '/signed-upload', bytes: 4 },
|
|
173
|
+
{
|
|
174
|
+
method: 'POST',
|
|
175
|
+
url: '/api/v1/media/med_1/finalize',
|
|
176
|
+
authorization: 'Bearer machine',
|
|
177
|
+
bytes: 0,
|
|
178
|
+
},
|
|
179
|
+
])
|
|
180
|
+
})
|
|
181
|
+
|
|
136
182
|
it('treats a truncated body as a transport failure, not a crash', async () => {
|
|
137
183
|
// A body that stops mid-JSON used to escape as a raw parse error outside
|
|
138
184
|
// the retry path, so nothing backed off and retried it.
|
package/src/client.ts
CHANGED
|
@@ -5,6 +5,7 @@ import type {
|
|
|
5
5
|
CapabilityDocument,
|
|
6
6
|
CreateMediaUploadRequestT,
|
|
7
7
|
CreateMediaUploadResponse,
|
|
8
|
+
FinalizeMediaUploadResponse,
|
|
8
9
|
EvidenceSnapshot,
|
|
9
10
|
GetAgentAcknowledgementResponse,
|
|
10
11
|
ListDevicesResponse,
|
|
@@ -84,6 +85,7 @@ export interface ApiClient {
|
|
|
84
85
|
options: { waitSeconds: number },
|
|
85
86
|
): Promise<GetAgentAcknowledgementResponse>
|
|
86
87
|
createMediaUpload(body: CreateMediaUploadRequestT): Promise<CreateMediaUploadResponse>
|
|
88
|
+
finalizeMediaUpload(mediaId: string): Promise<FinalizeMediaUploadResponse>
|
|
87
89
|
uploadMedia(grant: CreateMediaUploadResponse, bytes: Uint8Array): Promise<void>
|
|
88
90
|
health(): Promise<boolean>
|
|
89
91
|
}
|
|
@@ -248,6 +250,10 @@ export function createClient(
|
|
|
248
250
|
waitSeconds,
|
|
249
251
|
),
|
|
250
252
|
createMediaUpload: (body) => call('POST', '/api/v1/media', body),
|
|
253
|
+
// Storage-observed bytes become authoritative before a send can reference
|
|
254
|
+
// the upload; the caller's declared size is only a request.
|
|
255
|
+
finalizeMediaUpload: (mediaId) =>
|
|
256
|
+
call('POST', `/api/v1/media/${encodeURIComponent(mediaId)}/finalize`),
|
|
251
257
|
uploadMedia: async (grant, bytes) => {
|
|
252
258
|
let response: Response
|
|
253
259
|
try {
|
|
@@ -265,6 +271,13 @@ export function createClient(
|
|
|
265
271
|
if (!response.ok) {
|
|
266
272
|
throw new NetworkError(`Upload rejected with status ${response.status}`)
|
|
267
273
|
}
|
|
274
|
+
// A successful storage PUT is not authorization to reference the media.
|
|
275
|
+
// The idempotent finalize call makes the server inspect provider metadata
|
|
276
|
+
// and commit authoritative quota before the command can use media_id.
|
|
277
|
+
await call<FinalizeMediaUploadResponse>(
|
|
278
|
+
'POST',
|
|
279
|
+
`/api/v1/media/${encodeURIComponent(grant.media_id)}/finalize`,
|
|
280
|
+
)
|
|
268
281
|
},
|
|
269
282
|
health: async () => {
|
|
270
283
|
try {
|
package/src/commands.test.ts
CHANGED
|
@@ -2392,12 +2392,7 @@ describe('interactive command UX', () => {
|
|
|
2392
2392
|
const deps: CommandDeps = {
|
|
2393
2393
|
...makeDeps(io, client),
|
|
2394
2394
|
cwd,
|
|
2395
|
-
env:
|
|
2396
|
-
XDG_CONFIG_HOME: path.join(cwd, 'config'),
|
|
2397
|
-
XDG_STATE_HOME: path.join(cwd, 'state'),
|
|
2398
|
-
CODEX_HOME: path.join(cwd, 'codex'),
|
|
2399
|
-
CLAUDE_CONFIG_DIR: path.join(cwd, 'claude'),
|
|
2400
|
-
},
|
|
2395
|
+
env: isolatedEnv(cwd),
|
|
2401
2396
|
store: { load: () => null, save: () => {}, clear: () => {}, describe: () => 'empty store' },
|
|
2402
2397
|
}
|
|
2403
2398
|
|
|
@@ -2993,19 +2988,16 @@ describe('init', () => {
|
|
|
2993
2988
|
const deps = {
|
|
2994
2989
|
...makeDeps(io, client),
|
|
2995
2990
|
cwd,
|
|
2996
|
-
env:
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
CLAUDE_CONFIG_DIR: path.join(cwd, 'claude'),
|
|
3002
|
-
OPENCODE_CONFIG_DIR: path.join(cwd, 'opencode'),
|
|
3003
|
-
},
|
|
2991
|
+
env: isolatedEnv(cwd),
|
|
2992
|
+
nativeSkills: {
|
|
2993
|
+
list: async () => ({ skills: [] }),
|
|
2994
|
+
add: async () => 0,
|
|
2995
|
+
} satisfies NativeSkills,
|
|
3004
2996
|
}
|
|
3005
2997
|
|
|
3006
2998
|
expect(await initCommand(deps, {})).toBe(EXIT.ok)
|
|
3007
|
-
expect(asked.
|
|
3008
|
-
expect(asked.
|
|
2999
|
+
expect(asked).toEqual(expect.arrayContaining([expect.stringMatching(/hooks/)]))
|
|
3000
|
+
expect(asked).toEqual(expect.arrayContaining([expect.stringMatching(/skill/)]))
|
|
3009
3001
|
const out = io.outLines.join('\n')
|
|
3010
3002
|
expect(out).toContain('Next: Your devices')
|
|
3011
3003
|
// Device wait prompts only after optionals have been considered.
|
package/src/hooks.test.ts
CHANGED