@pouchy_ai/admin-sdk 0.9.0 → 0.10.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 +25 -0
- package/README.md +56 -0
- package/dist/index.d.ts +42 -5
- package/dist/index.js +48 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `@pouchy_ai/admin-sdk` are documented here.
|
|
4
4
|
|
|
5
|
+
## 0.10.0 — 2026-07-29
|
|
6
|
+
|
|
7
|
+
- **`AdminApiError.code` — the Admin API's machine-readable failure tag.** The
|
|
8
|
+
server has been sending `code` on ten failures across nineteen emit sites;
|
|
9
|
+
this package read `error` off the body and dropped `code` on the floor, so the
|
|
10
|
+
field was unreachable from the first-party client.
|
|
11
|
+
- **Why it matters more than it sounds: every one of those ten codes is a 409.**
|
|
12
|
+
On this plane the status line never disambiguates, and two of the durable-run
|
|
13
|
+
routes answer 409 for two different reasons each, with opposite recoveries —
|
|
14
|
+
`resumeRun` (`stale_token`: re-read and answer again / `run_not_parked`: stop,
|
|
15
|
+
someone already decided) and `signalRun` (`event_mismatch`: your event is early
|
|
16
|
+
or wrong, retry later / `run_not_waiting`: the wait ended, stop). This
|
|
17
|
+
package's own doc comments told callers to make exactly those distinctions,
|
|
18
|
+
which was not possible without string-matching English prose.
|
|
19
|
+
- **`ADMIN_ERROR_CODES` + `AdminErrorCode` are exported** so a `switch` is typed.
|
|
20
|
+
The type carries a `(string & {})` arm (same doctrine as the companion SDK's
|
|
21
|
+
`CompanionErrorCodeValue`), so a code newer than your installed build still
|
|
22
|
+
arrives as a readable string instead of being filtered out. The list is
|
|
23
|
+
drift-gated against the server's own vocabulary in both directions.
|
|
24
|
+
- Additive only — no method signature, request shape, status code or error
|
|
25
|
+
message changed. `code` is `undefined` on every failure the server did not tag
|
|
26
|
+
(400s, 404s, the 429, transport errors), and a non-string or empty `code` on
|
|
27
|
+
the wire leaves it `undefined` rather than stamping a falsy value a `switch`
|
|
28
|
+
would fall through on.
|
|
29
|
+
|
|
5
30
|
## 0.9.0 — 2026-07-28
|
|
6
31
|
|
|
7
32
|
- **Channel CRUD is typed (CR-77).** `createChannel` takes
|
package/README.md
CHANGED
|
@@ -100,6 +100,62 @@ try {
|
|
|
100
100
|
}
|
|
101
101
|
```
|
|
102
102
|
|
|
103
|
+
### Failure codes (409)
|
|
104
|
+
|
|
105
|
+
**Every machine-readable failure on this API is a 409**, so `status` separates
|
|
106
|
+
none of them. `AdminApiError.code` is the discriminator — switch on it rather
|
|
107
|
+
than string-matching `message`, which is prose and may be reworded:
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
import { AdminApiError } from '@pouchy_ai/admin-sdk';
|
|
111
|
+
|
|
112
|
+
// `runId` and `token` come from the run you are answering — the
|
|
113
|
+
// `agent.run_awaiting` webhook carries both, or read them off `getRun`.
|
|
114
|
+
declare const runId: string;
|
|
115
|
+
declare const token: string;
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
await admin.resumeRun(runId, { approved: true, token });
|
|
119
|
+
} catch (e) {
|
|
120
|
+
if (!(e instanceof AdminApiError)) throw e;
|
|
121
|
+
switch (e.code) {
|
|
122
|
+
case 'stale_token': {
|
|
123
|
+
// The run moved on to a different question. Re-read and answer the
|
|
124
|
+
// current one — this is the retryable branch.
|
|
125
|
+
const { run } = await admin.getRun(runId);
|
|
126
|
+
if (run.awaiting) await admin.resumeRun(runId, { approved: true, token: run.awaiting.token });
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
case 'run_not_parked':
|
|
130
|
+
// Someone else decided, or the run was cancelled. Do NOT retry.
|
|
131
|
+
break;
|
|
132
|
+
default:
|
|
133
|
+
throw e;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
The vocabulary is exported as `ADMIN_ERROR_CODES` (and the `AdminErrorCode`
|
|
139
|
+
type), and is append-only:
|
|
140
|
+
|
|
141
|
+
| code | route | what to do |
|
|
142
|
+
|---|---|---|
|
|
143
|
+
| `schedule_limit_reached` | `createSchedule` | delete or disable a schedule, retry |
|
|
144
|
+
| `channel_limit_reached` | `createChannel` | delete an unused connector, retry |
|
|
145
|
+
| `webhook_limit_reached` | `createWebhook` | delete an unused endpoint, retry |
|
|
146
|
+
| `run_limit_reached` | `createRun` | wait for a run to finish, or `cancelRun` one |
|
|
147
|
+
| `reembed_required` | knowledge config | clear + re-ingest to switch embedding model |
|
|
148
|
+
| `run_terminal` | `cancelRun` | the run already finished — nothing to cancel |
|
|
149
|
+
| `run_not_parked` | `resumeRun` | already decided; stop retrying |
|
|
150
|
+
| `stale_token` | `resumeRun` | `getRun`, then answer the current `awaiting.token` |
|
|
151
|
+
| `run_not_waiting` | `signalRun` | the wait ended; stop retrying |
|
|
152
|
+
| `event_mismatch` | `signalRun` | the run wants a different key; your event is early or wrong |
|
|
153
|
+
|
|
154
|
+
`code` is `undefined` on every failure the server did not tag (400s, 404s, the
|
|
155
|
+
429, transport errors), so always keep a `default` branch. The type has a
|
|
156
|
+
`(string & {})` arm on purpose: a code newer than your installed build still
|
|
157
|
+
arrives as a readable string. Available from **0.10.0**.
|
|
158
|
+
|
|
103
159
|
### Throttling (429)
|
|
104
160
|
|
|
105
161
|
Writes are rate-limited per IP — 120 non-`GET` requests per minute across the
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const ADMIN_SDK_VERSION = "0.
|
|
1
|
+
export declare const ADMIN_SDK_VERSION = "0.10.0";
|
|
2
2
|
export declare const DEFAULT_BASE_URL = "https://pouchy.ai/v1/admin";
|
|
3
3
|
/** Deadline for the routes whose server handler declares `maxDuration: 300` —
|
|
4
4
|
* the server's own ceiling plus headroom, so a client abort can only ever mean
|
|
@@ -50,9 +50,33 @@ export interface AdminClientOptions {
|
|
|
50
50
|
* failed with the self-contradicting `request timed out after 0ms`. */
|
|
51
51
|
timeoutMs?: number;
|
|
52
52
|
}
|
|
53
|
+
/** Machine-readable failure tags the Admin API puts on an error body, mirroring
|
|
54
|
+
* the server's own list (`src/lib/server/platform/api-error-codes.ts`) and
|
|
55
|
+
* drift-tested against it. APPEND-ONLY: these are documented as switchable, so
|
|
56
|
+
* renaming or removing one breaks integrators.
|
|
57
|
+
*
|
|
58
|
+
* Every member is a **409**. That is the whole reason this vocabulary is
|
|
59
|
+
* published: on this plane the status line never disambiguates, so two of the
|
|
60
|
+
* durable-run routes answer 409 for two entirely different reasons each, with
|
|
61
|
+
* different recoveries. `code` is the only thing that tells them apart. */
|
|
62
|
+
export declare const ADMIN_ERROR_CODES: readonly ["schedule_limit_reached", "channel_limit_reached", "webhook_limit_reached", "run_limit_reached", "reembed_required", "run_terminal", "run_not_parked", "stale_token", "run_not_waiting", "event_mismatch"];
|
|
63
|
+
/** `AdminApiError.code` values. The `(string & {})` arm keeps the type open for
|
|
64
|
+
* codes newer than this SDK build while preserving autocomplete — same
|
|
65
|
+
* doctrine as the companion SDK's `CompanionErrorCodeValue`. */
|
|
66
|
+
export type AdminErrorCode = (typeof ADMIN_ERROR_CODES)[number] | (string & {});
|
|
53
67
|
/** Thrown on any non-2xx response. `status` is the HTTP status; `message` is the
|
|
54
68
|
* server's `error` string when present; `retryAfter` is the throttle's own
|
|
55
|
-
* backoff in SECONDS on a 429 (undefined on every other failure)
|
|
69
|
+
* backoff in SECONDS on a 429 (undefined on every other failure); `code` is the
|
|
70
|
+
* server's machine tag when it named one (undefined otherwise).
|
|
71
|
+
*
|
|
72
|
+
* Why `code` exists: the Admin API's ten machine-readable failures are ALL on
|
|
73
|
+
* 409, so `status` cannot separate them and this package used to discard the
|
|
74
|
+
* one field that can. Two routes are the point — `resumeRun` answers 409 for
|
|
75
|
+
* `run_not_parked` (someone already decided; re-read, do not retry) or
|
|
76
|
+
* `stale_token` (your token aged out; re-read for the current one and answer
|
|
77
|
+
* again), and `signalRun` answers 409 for `run_not_waiting` or
|
|
78
|
+
* `event_mismatch`. This package's own docs told callers to distinguish those,
|
|
79
|
+
* which was not possible without switching on prose.
|
|
56
80
|
*
|
|
57
81
|
* Why `retryAfter` exists: the entire write surface of this SDK sits behind one
|
|
58
82
|
* per-IP throttle — `hooks.server.ts` runs a `v1-admin-write` bucket (120
|
|
@@ -69,7 +93,8 @@ export interface AdminClientOptions {
|
|
|
69
93
|
export declare class AdminApiError extends Error {
|
|
70
94
|
status: number;
|
|
71
95
|
readonly retryAfter?: number;
|
|
72
|
-
|
|
96
|
+
readonly code?: AdminErrorCode;
|
|
97
|
+
constructor(message: string, status: number, retryAfter?: number, code?: string);
|
|
73
98
|
}
|
|
74
99
|
export type Env = 'live' | 'test';
|
|
75
100
|
export type AgentStatus = 'draft' | 'published';
|
|
@@ -628,7 +653,13 @@ export interface AdminClient {
|
|
|
628
653
|
* `awaiting.token` (also carried by the `agent.run_awaiting` webhook) and
|
|
629
654
|
* fences the decision against a stale or replayed answer. Without this call
|
|
630
655
|
* a parked run never resumes: it holds no lease and has left the platform's
|
|
631
|
-
* due window by design.
|
|
656
|
+
* due window by design.
|
|
657
|
+
*
|
|
658
|
+
* Both failures here are **409**; switch on `AdminApiError.code` to tell
|
|
659
|
+
* them apart. `stale_token` — the run has moved on to a different question:
|
|
660
|
+
* re-read it with `getRun` and answer the current `awaiting.token`.
|
|
661
|
+
* `run_not_parked` — it is no longer awaiting anyone (someone else decided,
|
|
662
|
+
* or it was cancelled): do NOT retry. */
|
|
632
663
|
resumeRun(runId: string, input: {
|
|
633
664
|
approved: boolean;
|
|
634
665
|
token: string;
|
|
@@ -640,7 +671,13 @@ export interface AdminClient {
|
|
|
640
671
|
}>;
|
|
641
672
|
/** Deliver an external event to a run parked on one. `event` must equal the
|
|
642
673
|
* key the run is waiting for — a mismatch answers 409 `event_mismatch`, so a
|
|
643
|
-
* webhook retry can tell "too late" from "rejected".
|
|
674
|
+
* webhook retry can tell "too late" from "rejected".
|
|
675
|
+
*
|
|
676
|
+
* Read that verdict off `AdminApiError.code` (0.10.0; before that the field
|
|
677
|
+
* was dropped and this advice was unfollowable). `event_mismatch` — the run
|
|
678
|
+
* wants a DIFFERENT key, so your event is early or wrong: safe to retry
|
|
679
|
+
* later. `run_not_waiting` — the run is not parked on an event at all, so
|
|
680
|
+
* the wait already ended: stop retrying. */
|
|
644
681
|
signalRun(runId: string, input: {
|
|
645
682
|
event: string;
|
|
646
683
|
payload?: unknown;
|
package/dist/index.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// import { createAdminClient } from '@pouchy_ai/admin-sdk';
|
|
9
9
|
// const admin = createAdminClient({ adminKey: process.env.POUCHY_ADMIN_KEY! });
|
|
10
10
|
// const { agents } = await admin.listAgents();
|
|
11
|
-
export const ADMIN_SDK_VERSION = '0.
|
|
11
|
+
export const ADMIN_SDK_VERSION = '0.10.0';
|
|
12
12
|
export const DEFAULT_BASE_URL = 'https://pouchy.ai/v1/admin';
|
|
13
13
|
/** Default per-request timeout (ms). A hung upstream otherwise never rejects. */
|
|
14
14
|
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
@@ -55,9 +55,40 @@ export function requestDeadlineMs(method, path) {
|
|
|
55
55
|
? LONG_WORK_TIMEOUT_MS
|
|
56
56
|
: DEFAULT_TIMEOUT_MS;
|
|
57
57
|
}
|
|
58
|
+
/** Machine-readable failure tags the Admin API puts on an error body, mirroring
|
|
59
|
+
* the server's own list (`src/lib/server/platform/api-error-codes.ts`) and
|
|
60
|
+
* drift-tested against it. APPEND-ONLY: these are documented as switchable, so
|
|
61
|
+
* renaming or removing one breaks integrators.
|
|
62
|
+
*
|
|
63
|
+
* Every member is a **409**. That is the whole reason this vocabulary is
|
|
64
|
+
* published: on this plane the status line never disambiguates, so two of the
|
|
65
|
+
* durable-run routes answer 409 for two entirely different reasons each, with
|
|
66
|
+
* different recoveries. `code` is the only thing that tells them apart. */
|
|
67
|
+
export const ADMIN_ERROR_CODES = [
|
|
68
|
+
'schedule_limit_reached',
|
|
69
|
+
'channel_limit_reached',
|
|
70
|
+
'webhook_limit_reached',
|
|
71
|
+
'run_limit_reached',
|
|
72
|
+
'reembed_required',
|
|
73
|
+
'run_terminal',
|
|
74
|
+
'run_not_parked',
|
|
75
|
+
'stale_token',
|
|
76
|
+
'run_not_waiting',
|
|
77
|
+
'event_mismatch'
|
|
78
|
+
];
|
|
58
79
|
/** Thrown on any non-2xx response. `status` is the HTTP status; `message` is the
|
|
59
80
|
* server's `error` string when present; `retryAfter` is the throttle's own
|
|
60
|
-
* backoff in SECONDS on a 429 (undefined on every other failure)
|
|
81
|
+
* backoff in SECONDS on a 429 (undefined on every other failure); `code` is the
|
|
82
|
+
* server's machine tag when it named one (undefined otherwise).
|
|
83
|
+
*
|
|
84
|
+
* Why `code` exists: the Admin API's ten machine-readable failures are ALL on
|
|
85
|
+
* 409, so `status` cannot separate them and this package used to discard the
|
|
86
|
+
* one field that can. Two routes are the point — `resumeRun` answers 409 for
|
|
87
|
+
* `run_not_parked` (someone already decided; re-read, do not retry) or
|
|
88
|
+
* `stale_token` (your token aged out; re-read for the current one and answer
|
|
89
|
+
* again), and `signalRun` answers 409 for `run_not_waiting` or
|
|
90
|
+
* `event_mismatch`. This package's own docs told callers to distinguish those,
|
|
91
|
+
* which was not possible without switching on prose.
|
|
61
92
|
*
|
|
62
93
|
* Why `retryAfter` exists: the entire write surface of this SDK sits behind one
|
|
63
94
|
* per-IP throttle — `hooks.server.ts` runs a `v1-admin-write` bucket (120
|
|
@@ -74,14 +105,27 @@ export function requestDeadlineMs(method, path) {
|
|
|
74
105
|
export class AdminApiError extends Error {
|
|
75
106
|
status;
|
|
76
107
|
retryAfter;
|
|
77
|
-
|
|
108
|
+
code;
|
|
109
|
+
constructor(message, status, retryAfter, code) {
|
|
78
110
|
super(message);
|
|
79
111
|
this.name = 'AdminApiError';
|
|
80
112
|
this.status = status;
|
|
81
113
|
if (retryAfter !== undefined)
|
|
82
114
|
this.retryAfter = retryAfter;
|
|
115
|
+
// Only a non-empty string. A body carrying `code: null` or `code: 42`
|
|
116
|
+
// must leave the property ABSENT rather than stamping a falsy value that
|
|
117
|
+
// a `switch (e.code)` would fall through on in a way the caller can't see.
|
|
118
|
+
if (typeof code === 'string' && code)
|
|
119
|
+
this.code = code;
|
|
83
120
|
}
|
|
84
121
|
}
|
|
122
|
+
/** The `code` a non-2xx body named, or undefined. Kept beside `retryAfterFrom`
|
|
123
|
+
* and equally defensive: the body is parsed with a `.catch(() => ({}))`, so
|
|
124
|
+
* every field here is untrusted shape, not a typed response. */
|
|
125
|
+
function codeFrom(body) {
|
|
126
|
+
const v = body?.code;
|
|
127
|
+
return typeof v === 'string' && v ? v : undefined;
|
|
128
|
+
}
|
|
85
129
|
/** Seconds to wait before retrying a throttled response, or undefined when the
|
|
86
130
|
* server named neither a body field nor a header.
|
|
87
131
|
*
|
|
@@ -165,7 +209,7 @@ export function createAdminClient(opts) {
|
|
|
165
209
|
}
|
|
166
210
|
const data = (await res.json().catch(() => ({})));
|
|
167
211
|
if (!res.ok)
|
|
168
|
-
throw new AdminApiError(data?.error ?? `HTTP ${res.status}`, res.status, retryAfterFrom(res, data));
|
|
212
|
+
throw new AdminApiError(data?.error ?? `HTTP ${res.status}`, res.status, retryAfterFrom(res, data), codeFrom(data));
|
|
169
213
|
return data;
|
|
170
214
|
}
|
|
171
215
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pouchy_ai/admin-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Typed TypeScript client for the Pouchy Admin API \u2014 manage agents, keys, end users, knowledge, skills, channels, schedules, webhooks and credentials headlessly, with a project Admin key.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|