@pidgeon-ai/cli 2.1.0 → 2.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 +24 -0
- package/README.md +6 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +112 -34
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.4.0
|
|
4
|
+
|
|
5
|
+
- `pidgeon agents rename <id> <name>`, `pidgeon agents revoke <id>`,
|
|
6
|
+
`pidgeon keys revoke <agent-id> <kid>` and `pidgeon pods list|create|remove`.
|
|
7
|
+
- Lists that page (`agents`, `inboxes`, `mail list`, `search`, `threads
|
|
8
|
+
list`, `events`) take `--limit` and `--cursor`, and end with
|
|
9
|
+
`More: --cursor <next>` when there is more; they were cut off silently.
|
|
10
|
+
- `mail list` shows sent mail by who it went to, not the inbox's address.
|
|
11
|
+
- `agents create` and `inboxes create` take `--pod`.
|
|
12
|
+
- `keys proof` takes `--name`, so the key file names its agent.
|
|
13
|
+
- Signed requests carry a nonce and cover the query (SDK 2.4.0).
|
|
14
|
+
|
|
15
|
+
## 2.3.0
|
|
16
|
+
|
|
17
|
+
- `pidgeon webhooks create --inbox <id>...` takes several inboxes.
|
|
18
|
+
- `--days` must be a whole number of days; `abc` used to ask for 30.
|
|
19
|
+
- The help lists `--cc` and `--bcc`, and that `drafts create` takes the
|
|
20
|
+
same options as `send`.
|
|
21
|
+
|
|
22
|
+
## 2.2.0
|
|
23
|
+
|
|
24
|
+
- `pidgeon webhooks create --inbox <id>`: the endpoint hears only that
|
|
25
|
+
inbox's events; `webhooks list` shows an endpoint's inboxes.
|
|
26
|
+
|
|
3
27
|
## 2.1.0
|
|
4
28
|
|
|
5
29
|
- Email: `pidgeon mail list|read|wait`, `pidgeon search`, `pidgeon threads
|
package/README.md
CHANGED
|
@@ -17,6 +17,9 @@ or `PIDGEON_AGENT_KEY` (the file's `private_jwk`) and `PIDGEON_DIRECTORY`.
|
|
|
17
17
|
|
|
18
18
|
```sh
|
|
19
19
|
pidgeon agents show <agent-id> # its keys, and how websites see it
|
|
20
|
+
pidgeon agents rename <agent-id> "Table agent"
|
|
21
|
+
pidgeon keys revoke <agent-id> <kid> # requests signed with it stop verifying
|
|
22
|
+
pidgeon agents revoke <agent-id> # it and every key; cannot be undone
|
|
20
23
|
pidgeon inboxes create bookings --agent <agent-id>
|
|
21
24
|
pidgeon send <inbox-id> --to ana@example.com --subject "Table for two" --text "Friday at 8?"
|
|
22
25
|
pidgeon drafts create <inbox-id> --to ana@example.com --subject "…" --text "…"
|
|
@@ -28,10 +31,13 @@ pidgeon app-passwords create <inbox-id> "Apple Mail"
|
|
|
28
31
|
pidgeon sites create Shop --redirect https://shop.example/callback
|
|
29
32
|
pidgeon reports create <verification-id> --category spam --evidence "…"
|
|
30
33
|
pidgeon events
|
|
34
|
+
pidgeon pods list | create <name> | remove <pod-id>
|
|
31
35
|
pidgeon api GET /v1/email/rules # any /v1 call
|
|
32
36
|
```
|
|
33
37
|
|
|
34
38
|
`pidgeon --help` lists every command; `--json` prints the API's own answer.
|
|
39
|
+
A list prints a page at a time (`--limit`); when there is more, its last line
|
|
40
|
+
is the `--cursor` that reads the next page.
|
|
35
41
|
|
|
36
42
|
Signing in opens the browser once; the session is kept in
|
|
37
43
|
`~/.config/pidgeon/credentials.json` and refreshed. On a machine with no
|
package/dist/cli.d.ts
CHANGED
package/dist/cli.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import { parseArgs } from 'node:util';
|
|
10
10
|
import { PidgeonError } from '@pidgeon-ai/sdk';
|
|
11
11
|
import { addAgentKey, baseUrlOf, clientFromEnvironment, listAgentKeys, makeKeyProof, readSession, setUpAgent, signIn, signOut, } from '@pidgeon-ai/sdk/node';
|
|
12
|
-
export const VERSION = '2.
|
|
12
|
+
export const VERSION = '2.4.0';
|
|
13
13
|
const HELP = `pidgeon ${VERSION}: Pidgeon from a terminal.
|
|
14
14
|
|
|
15
15
|
Signing in
|
|
@@ -19,14 +19,17 @@ Signing in
|
|
|
19
19
|
Or set PIDGEON_API_KEY; it wins over a session. PIDGEON_BASE_URL points elsewhere.
|
|
20
20
|
|
|
21
21
|
Agents (Passport)
|
|
22
|
-
pidgeon agents list
|
|
23
|
-
pidgeon agents create <name> [--purpose <text>]
|
|
22
|
+
pidgeon agents list [--limit <n>] [--cursor <cursor>]
|
|
23
|
+
pidgeon agents create <name> [--purpose <text>] [--pod <pod-id>]
|
|
24
24
|
Registers the agent, makes its key on this machine, publishes the public
|
|
25
25
|
half signed with it, and keeps the key here (pidgeon keys list).
|
|
26
26
|
pidgeon agents show <agent-id> its keys and its operator's reputation
|
|
27
|
+
pidgeon agents rename <agent-id> <name> [--purpose <text>]
|
|
28
|
+
pidgeon agents revoke <agent-id> it and every key it has; cannot be undone
|
|
27
29
|
pidgeon keys create <agent-id> a new key for it, made here
|
|
28
30
|
pidgeon keys list the keys this machine keeps
|
|
29
|
-
pidgeon keys
|
|
31
|
+
pidgeon keys revoke <agent-id> <kid> requests signed with it stop verifying
|
|
32
|
+
pidgeon keys proof <agent-id> --directory <url> --url <publish-url> [--name <agent name>]
|
|
30
33
|
For the hosted MCP server: makes the key here, keeps it, and prints the
|
|
31
34
|
public half and the signed proof that publishing needs. No sign-in.
|
|
32
35
|
|
|
@@ -41,19 +44,21 @@ Sign in with Pidgeon (Auth)
|
|
|
41
44
|
pidgeon mandates request --agent <id> --client <client-id> --scope <scope> --description <text> [--days <n>]
|
|
42
45
|
|
|
43
46
|
Email
|
|
44
|
-
pidgeon inboxes list
|
|
45
|
-
pidgeon inboxes create <name> [--kind agent|events] [--agent <id>] [--domain <your domain>]
|
|
47
|
+
pidgeon inboxes list [--limit <n>] [--cursor <cursor>]
|
|
48
|
+
pidgeon inboxes create <name> [--kind agent|events] [--agent <id>] [--domain <your domain>] [--pod <pod-id>]
|
|
46
49
|
pidgeon inboxes remove <inbox-id>
|
|
47
|
-
pidgeon send <inbox-id> --to <address>... --
|
|
48
|
-
|
|
50
|
+
pidgeon send <inbox-id> --to <address>... [--cc <address>...] [--bcc <address>...] --subject <text>
|
|
51
|
+
(--text <body> | --html <body>) [--reply-to <message-id>]
|
|
52
|
+
pidgeon drafts create <inbox-id> the same options as send
|
|
49
53
|
pidgeon drafts show|remove <draft-id>
|
|
50
54
|
pidgeon drafts send <draft-id> [--at <ISO time>]
|
|
51
55
|
pidgeon drafts approval <draft-id> hold it for a person to send or reject
|
|
52
|
-
pidgeon mail list <inbox-id>
|
|
56
|
+
pidgeon mail list <inbox-id> [--limit <n>] [--cursor <cursor>] the inbox's mail, newest first
|
|
53
57
|
pidgeon mail read <message-id> [--original] cleaned by the Firewall unless --original
|
|
54
58
|
pidgeon mail wait <inbox-id> [--since <time|message-id>] [--timeout <seconds>]
|
|
55
|
-
pidgeon search <words> [--inbox <inbox-id>]
|
|
59
|
+
pidgeon search <words> [--inbox <inbox-id>] [--limit <n>] [--cursor <cursor>]
|
|
56
60
|
pidgeon threads list [--inbox <inbox-id>] [--folder inbox|archive|spam|trash]
|
|
61
|
+
[--limit <n>] [--cursor <cursor>]
|
|
57
62
|
pidgeon threads show <thread-id>
|
|
58
63
|
pidgeon threads file <thread-id> --folder inbox|archive|spam|trash
|
|
59
64
|
pidgeon threads hand-over <thread-id> to a person: owner human, status human_review
|
|
@@ -61,10 +66,14 @@ Email
|
|
|
61
66
|
pidgeon app-passwords list <inbox-id> | revoke <app-password-id>
|
|
62
67
|
|
|
63
68
|
The account
|
|
64
|
-
pidgeon events [--type <type>] [--limit <n>]
|
|
65
|
-
pidgeon
|
|
69
|
+
pidgeon events [--type <type>] [--limit <n>] [--cursor <cursor>]
|
|
70
|
+
pidgeon pods list | create <name> | remove <pod-id>
|
|
71
|
+
pidgeon webhooks list | create <url> --event <type>... [--inbox <id>...] | remove <webhook-id>
|
|
66
72
|
pidgeon api <GET|POST|PATCH|DELETE> <path> [--data <json>] any /v1 call
|
|
67
73
|
|
|
74
|
+
A list prints a page at a time; when there is more, its last line is the
|
|
75
|
+
--cursor that reads the next page.
|
|
76
|
+
|
|
68
77
|
Add --json to any command for the API's own JSON.`;
|
|
69
78
|
const OPTIONS = {
|
|
70
79
|
json: { type: 'boolean' },
|
|
@@ -92,7 +101,7 @@ const OPTIONS = {
|
|
|
92
101
|
original: { type: 'boolean' },
|
|
93
102
|
since: { type: 'string' },
|
|
94
103
|
timeout: { type: 'string' },
|
|
95
|
-
inbox: { type: 'string' },
|
|
104
|
+
inbox: { type: 'string', multiple: true },
|
|
96
105
|
folder: { type: 'string' },
|
|
97
106
|
event: { type: 'string', multiple: true },
|
|
98
107
|
method: { type: 'string' },
|
|
@@ -104,6 +113,8 @@ const OPTIONS = {
|
|
|
104
113
|
client: { type: 'string' },
|
|
105
114
|
description: { type: 'string' },
|
|
106
115
|
days: { type: 'string' },
|
|
116
|
+
name: { type: 'string' },
|
|
117
|
+
cursor: { type: 'string' },
|
|
107
118
|
};
|
|
108
119
|
class Usage extends Error {
|
|
109
120
|
}
|
|
@@ -148,6 +159,18 @@ function need(value, what) {
|
|
|
148
159
|
throw new Usage(`Say which ${what}.`);
|
|
149
160
|
return value;
|
|
150
161
|
}
|
|
162
|
+
/** An option given at most once. */
|
|
163
|
+
function one(values, flag) {
|
|
164
|
+
if (values && values.length > 1)
|
|
165
|
+
throw new Usage(`Give ${flag} once here.`);
|
|
166
|
+
return values?.[0];
|
|
167
|
+
}
|
|
168
|
+
function wholeDays(value) {
|
|
169
|
+
const days = Number(value);
|
|
170
|
+
if (!Number.isInteger(days) || days < 1)
|
|
171
|
+
throw new Usage('--days is a whole number of days.');
|
|
172
|
+
return days;
|
|
173
|
+
}
|
|
151
174
|
function message(values) {
|
|
152
175
|
if (!values.to?.length)
|
|
153
176
|
throw new Usage('Send it to someone: --to <address>.');
|
|
@@ -176,6 +199,10 @@ async function dispatch(positionals, values, io) {
|
|
|
176
199
|
for (const line of plain())
|
|
177
200
|
io.out(line);
|
|
178
201
|
};
|
|
202
|
+
/** A page's lines, and how to read the next page when there is one. */
|
|
203
|
+
const paged = (page, lines) => page.next_cursor ? [...lines, `More: --cursor ${page.next_cursor}`] : lines;
|
|
204
|
+
const cursor = values.cursor;
|
|
205
|
+
const limit = values.limit ? Number(values.limit) : undefined;
|
|
179
206
|
switch (command) {
|
|
180
207
|
case 'login': {
|
|
181
208
|
const scope = (values.scope ?? 'admin');
|
|
@@ -222,14 +249,14 @@ async function dispatch(positionals, values, io) {
|
|
|
222
249
|
case 'agents': {
|
|
223
250
|
const pidgeon = client();
|
|
224
251
|
if (sub === 'list') {
|
|
225
|
-
const page = await pidgeon.agents.list();
|
|
252
|
+
const page = await pidgeon.agents.list({ cursor, limit });
|
|
226
253
|
show(page, () => page.data.length
|
|
227
|
-
? page.data.map((a) => `${a.id} ${a.status.padEnd(7)} ${a.name}`)
|
|
254
|
+
? paged(page, page.data.map((a) => `${a.id} ${a.status.padEnd(7)} ${a.name}`))
|
|
228
255
|
: ['No agents yet: pidgeon agents create <name>.']);
|
|
229
256
|
}
|
|
230
257
|
else if (sub === 'create') {
|
|
231
258
|
const name = need(arg, 'name to give the agent');
|
|
232
|
-
const made = await setUpAgent(pidgeon, { name, purpose: values.purpose }, io.env);
|
|
259
|
+
const made = await setUpAgent(pidgeon, { name, purpose: values.purpose, podId: values.pod }, io.env);
|
|
233
260
|
show({ agent: made.agent, kid: made.kid, key_file: made.keyFile }, () => [
|
|
234
261
|
`Registered ${made.agent.name} (${made.agent.id}) and published its key ${made.kid}.`,
|
|
235
262
|
`Its private key is in ${made.keyFile}, on this machine only.`,
|
|
@@ -250,8 +277,25 @@ async function dispatch(positionals, values, io) {
|
|
|
250
277
|
...(agent.reputation.next ? [agent.reputation.next] : []),
|
|
251
278
|
]);
|
|
252
279
|
}
|
|
280
|
+
else if (sub === 'rename') {
|
|
281
|
+
const id = need(arg, 'agent (its id)');
|
|
282
|
+
const name = positionals.slice(3).join(' ') || undefined;
|
|
283
|
+
if (!name && values.purpose === undefined)
|
|
284
|
+
throw new Usage('pidgeon agents rename <agent-id> <name> [--purpose <text>]');
|
|
285
|
+
const agent = await pidgeon.agents.update(id, { name, purpose: values.purpose });
|
|
286
|
+
show(agent, () => [
|
|
287
|
+
`${agent.name} (${agent.id})${agent.purpose ? `: ${agent.purpose}` : ''}`,
|
|
288
|
+
]);
|
|
289
|
+
}
|
|
290
|
+
else if (sub === 'revoke') {
|
|
291
|
+
const id = need(arg, 'agent (its id)');
|
|
292
|
+
await pidgeon.agents.revoke(id);
|
|
293
|
+
show({ revoked: id }, () => [
|
|
294
|
+
'Revoked, with every key it had. No request it signs verifies again.',
|
|
295
|
+
]);
|
|
296
|
+
}
|
|
253
297
|
else
|
|
254
|
-
throw new Usage('pidgeon agents list | create <name> | show <id>');
|
|
298
|
+
throw new Usage('pidgeon agents list | create <name> | show <id> | rename <id> <name> | revoke <id>');
|
|
255
299
|
return;
|
|
256
300
|
}
|
|
257
301
|
case 'keys': {
|
|
@@ -271,20 +315,26 @@ async function dispatch(positionals, values, io) {
|
|
|
271
315
|
const agentId = need(arg, 'agent (its id)');
|
|
272
316
|
const directory = need(values.directory, 'directory (--directory)');
|
|
273
317
|
const publishUrl = values.url ?? `${baseUrl}/v1/agents/${encodeURIComponent(agentId)}/keys`;
|
|
274
|
-
const proof = await makeKeyProof({ agentId, directory, publishUrl }, io.env);
|
|
318
|
+
const proof = await makeKeyProof({ agentId, directory, publishUrl, name: values.name }, io.env);
|
|
275
319
|
// Always JSON: it is for a coding agent to hand to publish_agent_key.
|
|
276
320
|
io.out(JSON.stringify(proof, null, 2));
|
|
277
321
|
}
|
|
322
|
+
else if (sub === 'revoke') {
|
|
323
|
+
const agentId = need(arg, 'agent (its id)');
|
|
324
|
+
const kid = need(positionals[3], 'key (its kid: pidgeon agents show <agent-id>)');
|
|
325
|
+
await client().agents.revokeKey(agentId, kid);
|
|
326
|
+
show({ revoked: kid }, () => [`Revoked ${kid}. Requests signed with it stop verifying.`]);
|
|
327
|
+
}
|
|
278
328
|
else
|
|
279
|
-
throw new Usage('pidgeon keys create <agent-id> | list | proof <agent-id>');
|
|
329
|
+
throw new Usage('pidgeon keys create <agent-id> | list | proof <agent-id> | revoke <agent-id> <kid>');
|
|
280
330
|
return;
|
|
281
331
|
}
|
|
282
332
|
case 'inboxes': {
|
|
283
333
|
const pidgeon = client();
|
|
284
334
|
if (sub === 'list') {
|
|
285
|
-
const page = await pidgeon.inboxes.list();
|
|
335
|
+
const page = await pidgeon.inboxes.list({ cursor, limit });
|
|
286
336
|
show(page, () => page.data.length
|
|
287
|
-
? page.data.map((i) => `${i.id} ${i.kind.padEnd(6)} ${i.address}`)
|
|
337
|
+
? paged(page, page.data.map((i) => `${i.id} ${i.kind.padEnd(6)} ${i.address}`))
|
|
288
338
|
: ['No inboxes yet.']);
|
|
289
339
|
}
|
|
290
340
|
else if (sub === 'create') {
|
|
@@ -294,6 +344,7 @@ async function dispatch(positionals, values, io) {
|
|
|
294
344
|
kind,
|
|
295
345
|
agentId: values.agent,
|
|
296
346
|
domain: values.domain,
|
|
347
|
+
podId: values.pod,
|
|
297
348
|
});
|
|
298
349
|
show(inbox, () => [`Made ${inbox.address} (${inbox.id}).`]);
|
|
299
350
|
}
|
|
@@ -430,7 +481,7 @@ async function dispatch(positionals, values, io) {
|
|
|
430
481
|
clientId: need(values.client, 'site (--client, its client_id)'),
|
|
431
482
|
scope: need(values.scope, 'scope (--scope)'),
|
|
432
483
|
description: need(values.description, 'what for (--description)'),
|
|
433
|
-
days: values.days ?
|
|
484
|
+
days: values.days === undefined ? undefined : wholeDays(values.days),
|
|
434
485
|
});
|
|
435
486
|
show(made, () => [`Send the person this link to allow or decline: ${made.consent_url}`]);
|
|
436
487
|
}
|
|
@@ -444,10 +495,12 @@ async function dispatch(positionals, values, io) {
|
|
|
444
495
|
}
|
|
445
496
|
case 'mail': {
|
|
446
497
|
const pidgeon = client();
|
|
447
|
-
const line = (m) =>
|
|
498
|
+
const line = (m) =>
|
|
499
|
+
// Received mail names who sent it; sent mail, who it went to.
|
|
500
|
+
`${m.id} ${m.created_at.slice(0, 16)} ${m.direction === 'in' ? `← ${m.from.address}` : `→ ${m.to.join(', ')}`} ${m.is_held ? '[held for a person]' : (m.subject ?? '')}${m.direction === 'in' && !m.seen ? ' (unread)' : ''}`;
|
|
448
501
|
if (sub === 'list') {
|
|
449
|
-
const page = await pidgeon.messages.list(need(arg, 'inbox (its id)'));
|
|
450
|
-
show(page, () => (page.data.length ? page.data.map(line) : ['No mail yet.']));
|
|
502
|
+
const page = await pidgeon.messages.list(need(arg, 'inbox (its id)'), { cursor, limit });
|
|
503
|
+
show(page, () => (page.data.length ? paged(page, page.data.map(line)) : ['No mail yet.']));
|
|
451
504
|
}
|
|
452
505
|
else if (sub === 'read') {
|
|
453
506
|
const m = await pidgeon.messages.get(need(arg, 'message (its id)'), {
|
|
@@ -487,10 +540,12 @@ async function dispatch(positionals, values, io) {
|
|
|
487
540
|
case 'search': {
|
|
488
541
|
const words = positionals.slice(1).join(' ');
|
|
489
542
|
const page = await client().messages.search(need(words || undefined, 'words to look for'), {
|
|
490
|
-
inboxId: values.inbox,
|
|
543
|
+
inboxId: one(values.inbox, '--inbox'),
|
|
544
|
+
cursor,
|
|
545
|
+
limit,
|
|
491
546
|
});
|
|
492
547
|
show(page, () => page.data.length
|
|
493
|
-
? page.data.map((m) => `${m.id} ${m.created_at.slice(0, 10)} ${m.from.address} ${m.subject ?? ''}`)
|
|
548
|
+
? paged(page, page.data.map((m) => `${m.id} ${m.created_at.slice(0, 10)} ${m.from.address} ${m.subject ?? ''}`))
|
|
494
549
|
: ['Nothing found.']);
|
|
495
550
|
return;
|
|
496
551
|
}
|
|
@@ -501,11 +556,13 @@ async function dispatch(positionals, values, io) {
|
|
|
501
556
|
if (values.folder && !folders.includes(values.folder))
|
|
502
557
|
throw new Usage('--folder is inbox, archive, spam or trash.');
|
|
503
558
|
const page = await pidgeon.threads.list({
|
|
504
|
-
inboxId: values.inbox,
|
|
559
|
+
inboxId: one(values.inbox, '--inbox'),
|
|
505
560
|
folder: values.folder,
|
|
561
|
+
cursor,
|
|
562
|
+
limit,
|
|
506
563
|
});
|
|
507
564
|
show(page, () => page.data.length
|
|
508
|
-
? page.data.map((t) => `${t.id} ${t.folder.padEnd(7)} ${t.unread_count ? `${t.unread_count} unread ` : ''}${t.subject ?? '(held)'}`)
|
|
565
|
+
? paged(page, page.data.map((t) => `${t.id} ${t.folder.padEnd(7)} ${t.unread_count ? `${t.unread_count} unread ` : ''}${t.subject ?? '(held)'}`))
|
|
509
566
|
: ['No conversations.']);
|
|
510
567
|
}
|
|
511
568
|
else if (sub === 'show') {
|
|
@@ -562,13 +619,15 @@ async function dispatch(positionals, values, io) {
|
|
|
562
619
|
if (sub === 'list') {
|
|
563
620
|
const list = await pidgeon.webhooks.list();
|
|
564
621
|
show(list, () => list.data.length
|
|
565
|
-
? list.data.map((h) => `${h.id} ${h.url} ${h.events.join(', ')}`)
|
|
622
|
+
? list.data.map((h) => `${h.id} ${h.url} ${h.events.join(', ')}${h.inbox_ids ? ` (inboxes: ${h.inbox_ids.join(', ')})` : ''}`)
|
|
566
623
|
: ['No webhooks.']);
|
|
567
624
|
}
|
|
568
625
|
else if (sub === 'create') {
|
|
569
626
|
const made = await pidgeon.webhooks.create({
|
|
570
627
|
url: need(arg, 'url (https)'),
|
|
571
|
-
|
|
628
|
+
// The API refuses, by name, an event webhooks do not carry.
|
|
629
|
+
events: (values.event ?? []),
|
|
630
|
+
...(values.inbox?.length ? { inboxIds: values.inbox } : {}),
|
|
572
631
|
});
|
|
573
632
|
show(made, () => [`Made ${made.id}. Secret, shown once: ${made.secret}`]);
|
|
574
633
|
}
|
|
@@ -577,19 +636,38 @@ async function dispatch(positionals, values, io) {
|
|
|
577
636
|
show({ removed: arg }, () => ['Removed.']);
|
|
578
637
|
}
|
|
579
638
|
else
|
|
580
|
-
throw new Usage('pidgeon webhooks list | create <url> --event <type>... | remove <id>');
|
|
639
|
+
throw new Usage('pidgeon webhooks list | create <url> --event <type>... [--inbox <id>...] | remove <id>');
|
|
581
640
|
return;
|
|
582
641
|
}
|
|
583
642
|
case 'events': {
|
|
584
643
|
const page = await client().events.list({
|
|
585
644
|
type: values.type,
|
|
586
|
-
limit
|
|
645
|
+
limit,
|
|
646
|
+
cursor,
|
|
587
647
|
});
|
|
588
648
|
show(page, () => page.data.length
|
|
589
|
-
? page.data.map((e) => `${e.created_at} ${e.type}`)
|
|
649
|
+
? paged(page, page.data.map((e) => `${e.created_at} ${e.type}`))
|
|
590
650
|
: ['Nothing has happened yet.']);
|
|
591
651
|
return;
|
|
592
652
|
}
|
|
653
|
+
case 'pods': {
|
|
654
|
+
const pidgeon = client();
|
|
655
|
+
if (sub === 'list') {
|
|
656
|
+
const list = await pidgeon.pods.list();
|
|
657
|
+
show(list, () => list.data.map((p) => `${p.id} ${p.name}${p.default ? ' (default)' : ''}`));
|
|
658
|
+
}
|
|
659
|
+
else if (sub === 'create') {
|
|
660
|
+
const pod = await pidgeon.pods.create(need(positionals.slice(2).join(' ') || undefined, 'name'));
|
|
661
|
+
show(pod, () => [`Made ${pod.name} (${pod.id}).`]);
|
|
662
|
+
}
|
|
663
|
+
else if (sub === 'remove') {
|
|
664
|
+
await pidgeon.pods.remove(need(arg, 'pod (its id)'));
|
|
665
|
+
show({ removed: arg }, () => ['Removed.']);
|
|
666
|
+
}
|
|
667
|
+
else
|
|
668
|
+
throw new Usage('pidgeon pods list | create <name> | remove <pod-id>');
|
|
669
|
+
return;
|
|
670
|
+
}
|
|
593
671
|
case 'api': {
|
|
594
672
|
const method = (sub ?? '').toUpperCase();
|
|
595
673
|
if (!['GET', 'POST', 'PATCH', 'DELETE'].includes(method) || !arg?.startsWith('/v1/'))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pidgeon-ai/cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0",
|
|
4
4
|
"description": "Pidgeon from a terminal: sign in, set agents up with keys made on your machine, and send from their inboxes.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"LICENSE"
|
|
37
37
|
],
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@pidgeon-ai/sdk": "^2.
|
|
39
|
+
"@pidgeon-ai/sdk": "^2.4.0"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@types/node": "^24.10.1",
|