@pouchy_ai/admin-sdk 0.13.0 → 0.15.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 +48 -0
- package/README.md +38 -1
- package/dist/index.d.ts +93 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,54 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `@pouchy_ai/admin-sdk` are documented here.
|
|
4
4
|
|
|
5
|
+
## 0.15.0 — 2026-08-03
|
|
6
|
+
|
|
7
|
+
- **`MonthUsage` now types the whole meter, including credits and voice.** The
|
|
8
|
+
interface declared six fields (`month`, `mau`, `mauLimit`, `sessions`,
|
|
9
|
+
`tokensIn`, `tokensOut`) while `GET /usage` returns sixteen. The rest fell
|
|
10
|
+
through the `[k: string]: unknown` index signature, so they were reachable but
|
|
11
|
+
typed `unknown` — `usage.credits > limit` did not compile, and reading the
|
|
12
|
+
product's metered units from the typed client required a cast. Newly declared:
|
|
13
|
+
- `credits`, `creditsByDay`, `creditLimit` — the per-turn credit meter;
|
|
14
|
+
- `creditsVoice`, `voiceMs`, `voiceCalls` — the realtime-voice slice of
|
|
15
|
+
`credits` and the measured minutes behind it. Voice is the most expensive
|
|
16
|
+
metered unit, so this is the split worth alerting on;
|
|
17
|
+
- `mauTest`, `sessionsByDay`, `mauByDay`, `tokensByDay` — the test-key MAU
|
|
18
|
+
count and the per-day chart buckets.
|
|
19
|
+
|
|
20
|
+
Additive and backwards-compatible: the index signature is retained, and every
|
|
21
|
+
new field is optional so the package stays honest against a deployment that
|
|
22
|
+
predates one. No runtime change — this release is types and docs only.
|
|
23
|
+
|
|
24
|
+
A drift gate now pins the interface against the server's usage reader, so the
|
|
25
|
+
next field added server-side cannot silently skip the package again.
|
|
26
|
+
|
|
27
|
+
## 0.14.0 — 2026-08-02
|
|
28
|
+
|
|
29
|
+
- **Channel provider registration is now typed.** The server registers
|
|
30
|
+
`telegram`/`discord` connectors with their provider on create (Telegram
|
|
31
|
+
`setWebhook`, Discord slash command) and re-registers on a `rotate` or a
|
|
32
|
+
secret replacement, reporting the outcome as a `provision` object. The
|
|
33
|
+
package typed neither half, so the opt-out was unreachable and the outcome
|
|
34
|
+
unreadable:
|
|
35
|
+
- `createChannel` takes **`autoProvision?: boolean`**. This is the half that
|
|
36
|
+
mattered: `setWebhook` REPLACES rather than adds, so an integrator who
|
|
37
|
+
already owns their bot's webhook had no typed way to say "don't touch it"
|
|
38
|
+
— the closed input type made the flag an excess-property error, leaving
|
|
39
|
+
`request()` or a cast as the only route.
|
|
40
|
+
- `createChannel` / `updateChannel` return **`provision?:
|
|
41
|
+
ChannelProvisionResult`**, and `updateChannel` returns the rotate-only
|
|
42
|
+
`inboundUrl?`. A `failed` outcome on a rotate is the one that bites — the
|
|
43
|
+
provider still points at the URL the rotate just invalidated, and
|
|
44
|
+
`manualCommand` is the fix.
|
|
45
|
+
- `ChannelProvisionResult`, `ChannelProvisionStatus` and
|
|
46
|
+
`ChannelProvisionCode` are exported. Branch on `code` (a stable contract
|
|
47
|
+
vocabulary), never on `detail` (provider prose).
|
|
48
|
+
|
|
49
|
+
Purely additive: no existing signature narrowed, so 0.13.x code compiles
|
|
50
|
+
unchanged. `admin-sdk-provision.drift.test.ts` now binds both unions to
|
|
51
|
+
`channels/provision.ts` so the two copies cannot drift again.
|
|
52
|
+
|
|
5
53
|
## 0.13.0 — 2026-08-02
|
|
6
54
|
|
|
7
55
|
- **`getUsageHistory({ months? })`** over `GET /v1/admin/usage/history` —
|
package/README.md
CHANGED
|
@@ -37,9 +37,12 @@ await admin.updateAgent(agent.agentId, { status: 'published' });
|
|
|
37
37
|
const { key } = await admin.createKey({ label: 'prod-backend', env: 'live' });
|
|
38
38
|
console.log(key); // the plaintext token — shown ONCE
|
|
39
39
|
|
|
40
|
-
// Read this month's usage
|
|
40
|
+
// Read this month's usage — MAU, tokens, and the credit meter (all typed)
|
|
41
41
|
const { usage } = await admin.getUsage();
|
|
42
42
|
console.log(usage.mau, '/', usage.mauLimit, 'MAU');
|
|
43
|
+
console.log(usage.credits, '/', usage.creditLimit, 'credits');
|
|
44
|
+
// Voice is the priciest metered unit — watch its slice, not just the total
|
|
45
|
+
console.log(usage.creditsVoice, 'of those credits were', usage.voiceCalls, 'voice calls');
|
|
43
46
|
|
|
44
47
|
// Equip an agent with ANY skill — including a docs-only skill.md that has no
|
|
45
48
|
// `tools:` block — entirely via the API:
|
|
@@ -208,6 +211,40 @@ Channel types are checked at compile time: `createChannel` takes a
|
|
|
208
211
|
`internal-a2a`), and `secret` is a named `ChannelSecretInput`. Per-transport
|
|
209
212
|
`secret.extra` fields are listed in <https://pouchy.ai/docs/channel-setup>.
|
|
210
213
|
|
|
214
|
+
**Telegram and Discord register themselves.** On create — and again on a
|
|
215
|
+
`rotate: true` or a secret replacement, which invalidate what the provider
|
|
216
|
+
holds — the server calls Telegram's `setWebhook` / registers Discord's slash
|
|
217
|
+
command with `secret.token`, and reports the outcome as a
|
|
218
|
+
`ChannelProvisionResult` on `provision`. Two things to know:
|
|
219
|
+
|
|
220
|
+
- `setWebhook` **replaces** rather than adds. If you already manage your bot's
|
|
221
|
+
webhook, pass `autoProvision: false` or the create will overwrite it.
|
|
222
|
+
- The connector is durable before any provider call, so this never fails the
|
|
223
|
+
request — a `failed` outcome is reported, not thrown. Branch on `code` (a
|
|
224
|
+
stable vocabulary, `ChannelProvisionCode`), never on `detail`; `manualCommand`
|
|
225
|
+
is the equivalent curl with secrets left as `<PLACEHOLDER>`.
|
|
226
|
+
|
|
227
|
+
```ts
|
|
228
|
+
import { createAdminClient } from '@pouchy_ai/admin-sdk';
|
|
229
|
+
|
|
230
|
+
const admin = createAdminClient({ adminKey: process.env.POUCHY_ADMIN_KEY! });
|
|
231
|
+
|
|
232
|
+
const { connector, provision } = await admin.createChannel({
|
|
233
|
+
type: 'telegram',
|
|
234
|
+
agentId: process.env.AGENT_ID!,
|
|
235
|
+
secret: {
|
|
236
|
+
token: process.env.TELEGRAM_BOT_TOKEN!,
|
|
237
|
+
inboundSecret: process.env.TELEGRAM_SECRET_TOKEN!
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
if (provision?.status === 'failed') {
|
|
242
|
+
// The connector exists — only the webhook registration didn't land.
|
|
243
|
+
console.warn('register by hand:', provision.detail, provision.manualCommand);
|
|
244
|
+
}
|
|
245
|
+
console.log(connector.id);
|
|
246
|
+
```
|
|
247
|
+
|
|
211
248
|
Capability **signing-key management is deliberately not mirrored** here: the
|
|
212
249
|
one-time `pcsk_`/`pesk_` plaintext reveal stays a human act on the owner
|
|
213
250
|
plane (dashboard / owner API). `listCapabilities` returns masked key status
|
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.15.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
|
|
@@ -211,13 +211,52 @@ export interface Instance {
|
|
|
211
211
|
lastActiveAt: string;
|
|
212
212
|
suspended?: boolean;
|
|
213
213
|
}
|
|
214
|
+
/** The current month's meter, as `GET /usage` returns it.
|
|
215
|
+
*
|
|
216
|
+
* Every field the server sends is declared. The index signature stays for
|
|
217
|
+
* forward compatibility with a newer server, but it is a fallback, not the
|
|
218
|
+
* contract: reaching a field THROUGH it yields `unknown`, so
|
|
219
|
+
* `usage.credits > limit` does not compile and an integrator has to cast — the
|
|
220
|
+
* same untyped-surface friction the channel-provisioning gap caused. The
|
|
221
|
+
* credit/voice fields below are the ones that bit: they are the product's
|
|
222
|
+
* metered units, and they shipped server-side (and in the OpenAPI) while this
|
|
223
|
+
* interface still described only MAU and tokens.
|
|
224
|
+
*
|
|
225
|
+
* All but `month` are optional so the package stays honest against an OLDER
|
|
226
|
+
* deployment that predates a field — a self-hosted server on a prior release
|
|
227
|
+
* really can omit the voice trio, and typing them as required would be the
|
|
228
|
+
* package asserting something it cannot know. */
|
|
214
229
|
export interface MonthUsage {
|
|
215
230
|
month: string;
|
|
216
231
|
mau: number;
|
|
232
|
+
/** Sessions minted by TEST keys — metered but never gated. */
|
|
233
|
+
mauTest?: number;
|
|
234
|
+
/** The plan's monthly MAU cap. ACCOUNT-scoped (pooled across the owner's
|
|
235
|
+
* projects) while `mau` above is this project's slice, so `mau / mauLimit`
|
|
236
|
+
* understates consumption on a multi-project account — compare against the
|
|
237
|
+
* pooled figure from `getUsageHistory({ scope: 'account' })` when you need
|
|
238
|
+
* the number the mint gate actually enforces. */
|
|
217
239
|
mauLimit: number;
|
|
218
240
|
sessions: number;
|
|
219
241
|
tokensIn: number;
|
|
220
242
|
tokensOut: number;
|
|
243
|
+
/** Per-UTC-day buckets, `{ 'YYYY-MM-DD': n }` — for charts. */
|
|
244
|
+
sessionsByDay?: Record<string, number>;
|
|
245
|
+
mauByDay?: Record<string, number>;
|
|
246
|
+
tokensByDay?: Record<string, number>;
|
|
247
|
+
creditsByDay?: Record<string, number>;
|
|
248
|
+
/** Credits consumed this month: one per completed standard turn, the Pro
|
|
249
|
+
* multiplier per Pro turn, and the per-minute rate for realtime voice. */
|
|
250
|
+
credits?: number;
|
|
251
|
+
/** The VOICE slice of `credits`, and the measured minutes it came from.
|
|
252
|
+
* Project-scoped: the pooled account counter carries no per-source
|
|
253
|
+
* breakdown. Voice is by far the most expensive metered unit, so this is
|
|
254
|
+
* the split to watch before a bill surprises anyone. */
|
|
255
|
+
creditsVoice?: number;
|
|
256
|
+
voiceMs?: number;
|
|
257
|
+
voiceCalls?: number;
|
|
258
|
+
/** The plan's monthly credit allowance (account-level, like `mauLimit`). */
|
|
259
|
+
creditLimit?: number;
|
|
221
260
|
[k: string]: unknown;
|
|
222
261
|
}
|
|
223
262
|
/** One month of the usage-history series. Unlike `MonthUsage` (the current
|
|
@@ -307,6 +346,32 @@ export interface ChannelSecretInput {
|
|
|
307
346
|
/** Extra named credentials for transports whose auth needs more than the pair above. */
|
|
308
347
|
extra?: Record<string, string>;
|
|
309
348
|
}
|
|
349
|
+
/** Outcome class of the provider registration the server performs on create,
|
|
350
|
+
* and on a rotate / secret replacement. Mirrors the server's `ProvisionStatus`
|
|
351
|
+
* (`channels/provision.ts`); `admin-sdk-provision.drift.test.ts` binds them. */
|
|
352
|
+
export type ChannelProvisionStatus = 'ok' | 'skipped' | 'failed';
|
|
353
|
+
/** Machine-readable reason for a `ChannelProvisionResult`. The server treats
|
|
354
|
+
* this vocabulary as part of the response contract (the dashboard keys its
|
|
355
|
+
* copy off it), so branch on `code`, never on `detail`. */
|
|
356
|
+
export type ChannelProvisionCode = 'webhook_set' | 'command_registered' | 'unsupported_type' | 'no_token' | 'no_inbound_url' | 'not_requested' | 'invalid_command_name' | 'app_lookup_failed' | 'upstream_rejected' | 'upstream_unreachable';
|
|
357
|
+
/** What the provider said when the server registered this connector for you.
|
|
358
|
+
*
|
|
359
|
+
* Registration is an enrichment on top of a durable write: the connector
|
|
360
|
+
* exists whether or not the provider answered, so this NEVER turns a
|
|
361
|
+
* successful create into an error — a non-ok outcome is reported here instead.
|
|
362
|
+
* A `failed` result always carries `manualCommand`, the equivalent curl with
|
|
363
|
+
* every credential left as a `<PLACEHOLDER>`, so the fallback is the manual
|
|
364
|
+
* registration this feature replaced rather than a dead end. `detail` is built
|
|
365
|
+
* from the provider's own message and never contains a credential. */
|
|
366
|
+
export interface ChannelProvisionResult {
|
|
367
|
+
status: ChannelProvisionStatus;
|
|
368
|
+
code: ChannelProvisionCode;
|
|
369
|
+
/** Operator-facing detail, from the provider's message. Never a credential. */
|
|
370
|
+
detail?: string;
|
|
371
|
+
/** The equivalent command to run by hand, secrets left as `<PLACEHOLDER>`.
|
|
372
|
+
* Present on every non-ok outcome a human can act on. */
|
|
373
|
+
manualCommand?: string;
|
|
374
|
+
}
|
|
310
375
|
/** A durable run as the API returns it. Typed rather than `unknown` because
|
|
311
376
|
* the parked-run flow — read `status`, read `awaiting.token`, answer — is the
|
|
312
377
|
* SDK's most important interaction, and forcing a cast there would put the
|
|
@@ -659,17 +724,44 @@ export interface AdminClient {
|
|
|
659
724
|
/** Connector credentials (bot token, signing secret, …) — stored
|
|
660
725
|
* encrypted, never returned. */
|
|
661
726
|
secret?: ChannelSecretInput;
|
|
727
|
+
/** Default **true**. For `telegram`/`discord` the server registers the
|
|
728
|
+
* connector with the provider on create — Telegram `setWebhook(inboundUrl)`,
|
|
729
|
+
* Discord a slash command — using `secret.token`, and reports the outcome
|
|
730
|
+
* on `provision`. Set `false` if you already manage that registration:
|
|
731
|
+
* otherwise the create OVERWRITES your bot's existing webhook, since
|
|
732
|
+
* `setWebhook` replaces rather than adds. Every other transport ignores
|
|
733
|
+
* this (`provision.code: 'unsupported_type'`). */
|
|
734
|
+
autoProvision?: boolean;
|
|
662
735
|
}): Promise<{
|
|
663
736
|
connector: {
|
|
664
737
|
id: string;
|
|
665
738
|
};
|
|
666
739
|
inboundUrl: string;
|
|
740
|
+
/** Absent for transports that need no provider registration. */
|
|
741
|
+
provision?: ChannelProvisionResult;
|
|
667
742
|
}>;
|
|
668
743
|
getChannel(channelId: string): Promise<{
|
|
669
744
|
connector: unknown;
|
|
670
745
|
}>;
|
|
746
|
+
/** Patch a connector. Accepts `agentId`, `enabled`, `config`, `secret`,
|
|
747
|
+
* `rotate` and `autoProvision` (see `createChannel`).
|
|
748
|
+
*
|
|
749
|
+
* `rotate: true` re-signs the inbound URL and returns it ONCE on
|
|
750
|
+
* `inboundUrl` — which also invalidates the old one, so a telegram/discord
|
|
751
|
+
* connector is RE-REGISTERED with the provider on a rotate or a secret
|
|
752
|
+
* replacement (without that, the provider would keep delivering to a dead
|
|
753
|
+
* endpoint while the connector still looked healthy). Check `provision` on
|
|
754
|
+
* those two patches: a `failed` outcome means the provider still points at
|
|
755
|
+
* the old URL, and `manualCommand` is the fix. Pass `autoProvision: false`
|
|
756
|
+
* to suppress. Every other patch — enable/disable, rebind, config — leaves
|
|
757
|
+
* both fields absent. */
|
|
671
758
|
updateChannel(channelId: string, patch: Record<string, unknown>): Promise<{
|
|
672
759
|
connector: unknown;
|
|
760
|
+
/** Present only when the patch carried `rotate: true`. */
|
|
761
|
+
inboundUrl?: string;
|
|
762
|
+
/** Present only on a rotate or a secret replacement, for a provisionable
|
|
763
|
+
* transport, when not suppressed with `autoProvision: false`. */
|
|
764
|
+
provision?: ChannelProvisionResult;
|
|
673
765
|
}>;
|
|
674
766
|
deleteChannel(channelId: string): Promise<{
|
|
675
767
|
ok: boolean;
|
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.15.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;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pouchy_ai/admin-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.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",
|