@openemail/cli 0.0.1 → 0.0.3
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/README.md +1 -1
- package/openemail.js +165 -102
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -126,7 +126,7 @@ openemail temp read
|
|
|
126
126
|
openemail temp delete --yes
|
|
127
127
|
```
|
|
128
128
|
|
|
129
|
-
A disposable inbox needs no account and no key. `temp new` prints the address, and the CLI keeps the inbox's token so the other `temp` commands find it by its id or address. `temp watch --first` waits for the first message, which suits a script waiting for a sign-up code.
|
|
129
|
+
A disposable inbox needs no account and no key. `temp new` prints the address, and the CLI keeps the inbox's token so the other `temp` commands find it by its id or address. `temp watch --first` waits for the first message, which suits a script waiting for a sign-up code. `temp-mail extend` adds up to an hour, within 24 hours of the inbox being created, and the CLI keeps the new token it returns. `temp delete` moves the mail to the bin and forgets the token here, while the lease runs on until it expires.
|
|
130
130
|
|
|
131
131
|
### Every resource
|
|
132
132
|
```bash
|
package/openemail.js
CHANGED
|
@@ -344,7 +344,8 @@ const CLI_CALLBACK = {
|
|
|
344
344
|
APP_PATH: '/cli/callback',
|
|
345
345
|
LOOPBACK_PATH: '/callback',
|
|
346
346
|
LOOPBACK_HOST: '127.0.0.1',
|
|
347
|
-
PASTE_SEPARATOR: '.'
|
|
347
|
+
PASTE_SEPARATOR: '.'
|
|
348
|
+
};
|
|
348
349
|
const OAUTH_PARAMS = {
|
|
349
350
|
RESPONSE_TYPE: 'response_type',
|
|
350
351
|
CLIENT_ID: 'client_id',
|
|
@@ -672,7 +673,7 @@ const AUTH_MESSAGES = {
|
|
|
672
673
|
: `OpenEmail could not refresh your sign-in (status ${status}). Try again shortly.`,
|
|
673
674
|
SESSION_EXPIRED_PROFILE_NEXT: (name) => `Run \`openemail login --profile ${name}\` again.`,
|
|
674
675
|
SERVER_SAID: (reason) => `The server said: ${reason}`,
|
|
675
|
-
CLIENT_NAME: (host) =>
|
|
676
|
+
CLIENT_NAME: (host) => `${OAUTH_CLIENT.NAME_PREFIX}${host}`,
|
|
676
677
|
WHO_WORKSPACE: (workspace) => `workspace ${workspace}`,
|
|
677
678
|
WHO_KEY: (keyId) => `key ${keyId}`,
|
|
678
679
|
KEY_STAYS_VALID: (keyId) => `The key itself still works. Revoke it with \`openemail keys revoke ${keyId}\` if you no longer need it.`,
|
|
@@ -818,7 +819,6 @@ const DOCS_CHAT = {
|
|
|
818
819
|
};
|
|
819
820
|
const DOCS_PATHS = {
|
|
820
821
|
DOCS: '/docs',
|
|
821
|
-
DOCS_PREFIX: 'docs/',
|
|
822
822
|
MARKDOWN_EXTENSION: '.md',
|
|
823
823
|
INDEX: '/llms.txt',
|
|
824
824
|
ANCHOR: '#',
|
|
@@ -829,7 +829,8 @@ const DOCS_PATTERNS = {
|
|
|
829
829
|
LEADING_SLASHES: /^\/+/,
|
|
830
830
|
TRAILING_SLASHES: /\/+$/,
|
|
831
831
|
LINE_BREAK: /\r?\n/,
|
|
832
|
-
DOT_SEGMENT: /^\.{1,2}
|
|
832
|
+
DOT_SEGMENT: /^\.{1,2}$/,
|
|
833
|
+
DOCS_PREFIX: /^(?:[a-z]{2}\/)?docs(?:\/|$)/i
|
|
833
834
|
};
|
|
834
835
|
const DOCS_FETCH = {
|
|
835
836
|
ACCEPT: 'text/markdown, text/plain;q=0.9',
|
|
@@ -1111,7 +1112,7 @@ const BOOLEAN_TRUE_WORDS = new Set(['true', '1', 'yes', 'on']);
|
|
|
1111
1112
|
const BOOLEAN_FALSE_WORDS = new Set(['false', '0', 'no', 'off']);
|
|
1112
1113
|
const NEGATIVE_NUMBER_PATTERN = /^-\d+(\.\d+)?$/;
|
|
1113
1114
|
|
|
1114
|
-
var version$1 = "0.0.
|
|
1115
|
+
var version$1 = "0.0.3";
|
|
1115
1116
|
|
|
1116
1117
|
const VERSION$1 = version$1;
|
|
1117
1118
|
const CLI_NAME = 'openemail';
|
|
@@ -1244,7 +1245,7 @@ const SCRIPTING_ROWS = [
|
|
|
1244
1245
|
const HELP_JSON_SCHEMA_VERSION = 1;
|
|
1245
1246
|
const CLI_NAME_WORD_PATTERN = new RegExp(`(^|[^\\w-])${CLI_NAME}($|[^\\w-])`);
|
|
1246
1247
|
|
|
1247
|
-
var version = "0.0.
|
|
1248
|
+
var version = "0.0.7";
|
|
1248
1249
|
|
|
1249
1250
|
const BUILD_BASE_URL = 'https://api.openemail.uk';
|
|
1250
1251
|
|
|
@@ -3495,7 +3496,7 @@ const DomainsCreateAddressMethod = {
|
|
|
3495
3496
|
scopes: ['domains:write'],
|
|
3496
3497
|
parameters: [
|
|
3497
3498
|
{ name: 'id', type: 'string', required: true, description: 'Domain id from `list` or `create`, a UUID.' },
|
|
3498
|
-
{ name: 'body.localPart', type: 'string', required: true, description: 'The part in front of the @, 1 to 64 characters once trimmed, lower cased. Letters, digits and ! # $ % & \' * + / = ? ^ _
|
|
3499
|
+
{ name: 'body.localPart', type: 'string', required: true, description: 'The part in front of the @, 1 to 64 characters once trimmed, lower cased. Letters, digits, the backtick and ! # $ % & \' * + / = ? ^ _ { | } ~ - are allowed, and so are dots between them. `*` on its own is how the catch-all is written and is refused.' },
|
|
3499
3500
|
{ name: 'body.label', type: 'string | null', required: false, description: 'A name for the address shown in the app, trimmed, up to 120 characters. Leave it out, or send null or an empty string, for none.' },
|
|
3500
3501
|
{ name: 'options.signal', type: 'AbortSignal', required: false, description: 'Cancels the request.' },
|
|
3501
3502
|
{ name: 'options.apiKey', type: 'string', required: false, description: 'Overrides the client\'s API key for this call only.' }
|
|
@@ -7500,14 +7501,14 @@ const TempMailCreateMethod = {
|
|
|
7500
7501
|
slug: 'temp-mail/create',
|
|
7501
7502
|
signature: 'create(body?: TempInboxCreate, options?: InboxScope): Promise<CreatedTempInboxResource>',
|
|
7502
7503
|
summary: 'Create a disposable inbox and its access token',
|
|
7503
|
-
description: 'Mints a disposable address and resolves with the inbox plus its `token`. It needs no credential, and this
|
|
7504
|
+
description: 'Mints a disposable address and resolves with the inbox plus its `token`. It needs no credential, and only this call and `extend` return a token. The token is the lease itself, signed, and nothing about it is stored on the server, so a lost token cannot be recovered, and every other method except `listDomains` needs it. Store it before you show the address to anyone.\n\nThe body is optional and so is every field in it. With nothing, you get a 12 character generated local part on the first domain in the pool, leased for 60 minutes. A chosen `localPart` is lowercased and must be up to 64 letters, digits, dots, dashes or underscores, starting and ending with a letter or digit, or it is 422 `invalid_address`, and one longer than 64 characters is 422 `invalid_parameter`. Names such as `postmaster`, `abuse` and `support` are 422 `reserved_address`, and an install with no pooled domain answers 503 `not_configured`.\n\nNothing is rate limited and nothing reserves an address, so this never answers 429 or 409. A name you choose is issued to anyone who asks for it, and each of you reads the mail that reaches it from the start of your own lease. Leave `localPart` out when the mail should reach you alone.',
|
|
7504
7505
|
method: 'POST',
|
|
7505
7506
|
path: '/temp-mail/inboxes',
|
|
7506
7507
|
scopes: [],
|
|
7507
7508
|
auth: 'none',
|
|
7508
7509
|
parameters: [
|
|
7509
|
-
{ name: 'body.domain', type: 'string', required: false, description: 'A domain from `listDomains`. Omit it for
|
|
7510
|
-
{ name: 'body.localPart', type: 'string', required: false, description: 'The part before the @. Omit it for a generated one, which
|
|
7510
|
+
{ name: 'body.domain', type: 'string', required: false, description: 'A domain from `listDomains`. Omit it for the first one in the pool. Any other name is 422 `unknown_domain` rather than a silent substitute.' },
|
|
7511
|
+
{ name: 'body.localPart', type: 'string', required: false, description: 'The part before the @. Omit it for a generated one, which nobody else is likely to be issued.' },
|
|
7511
7512
|
{ name: 'body.ttlMinutes', type: 'number', required: false, description: 'Lease length from now, a whole number from 1 to 1440. Defaults to 60. Out of range is 422 `invalid_parameter`, not clamped.' },
|
|
7512
7513
|
{ name: 'options.signal', type: 'AbortSignal', required: false, description: 'Cancels the request.' },
|
|
7513
7514
|
{ name: 'options.inboxToken', type: 'string', required: false, description: 'Ignored and never sent. Creating an inbox is anonymous and hands you the token instead.' }
|
|
@@ -7516,8 +7517,8 @@ const TempMailCreateMethod = {
|
|
|
7516
7517
|
example: 'const temp = createTempMail()\n\nconst inbox = await temp.create({ ttlMinutes: 120 })\n\nconsole.log(inbox.address, inbox.expiresAt)\nconsole.log(inbox.token)',
|
|
7517
7518
|
notes: [
|
|
7518
7519
|
'Not retried automatically. A retry would mint a second inbox, and the first would be unreachable because its only token was in the lost response.',
|
|
7519
|
-
'
|
|
7520
|
-
'A lease of 1440 minutes reaches the 24 hour ceiling at once,
|
|
7520
|
+
'Nothing holds an address back after its lease ends or its inbox is deleted, so it can be issued again at once, to anybody.',
|
|
7521
|
+
'A lease of 1440 minutes reaches the 24 hour ceiling at once, yet the inbox still comes back with `extensionsLeft: 23`, because that counts calls rather than time. None of them can add a minute.',
|
|
7521
7522
|
'Unknown body keys are 422 `invalid_parameter`, while a body that is not valid JSON is treated as an empty one.'
|
|
7522
7523
|
]
|
|
7523
7524
|
};
|
|
@@ -7529,21 +7530,21 @@ const TempMailDeleteMessageMethod = {
|
|
|
7529
7530
|
slug: 'temp-mail/deleteMessage',
|
|
7530
7531
|
signature: 'deleteMessage(inboxId: string, messageId: string, options?: InboxScope): Promise<DeletedTempMessageResource>',
|
|
7531
7532
|
summary: 'Delete one message from a disposable inbox',
|
|
7532
|
-
description: '
|
|
7533
|
+
description: 'Moves the message, attachments and all, to the bin of the mailbox that runs the pool, and no lease lists or opens it again. Nothing in this SDK restores it.\n\n`messageCount` goes down by one. There is never a slot to free: an inbox keeps every message that reaches it, and `listMessages` pages through all of them.',
|
|
7533
7534
|
method: 'DELETE',
|
|
7534
7535
|
path: '/temp-mail/inboxes/{id}/messages/{messageId}',
|
|
7535
7536
|
scopes: [],
|
|
7536
7537
|
auth: 'inboxToken',
|
|
7537
7538
|
parameters: [
|
|
7538
|
-
{ name: 'inboxId', type: 'string', required: true, description: 'The `tinb_` id `create` returned. It
|
|
7539
|
-
{ name: 'messageId', type: 'string', required: true, description: '
|
|
7539
|
+
{ name: 'inboxId', type: 'string', required: true, description: 'The `tinb_` id `create` returned. It is not checked against the token, which alone decides the inbox.' },
|
|
7540
|
+
{ name: 'messageId', type: 'string', required: true, description: 'An `id` from `listMessages`, such as `thr_` and 24 hex.' },
|
|
7540
7541
|
{ name: 'options.signal', type: 'AbortSignal', required: false, description: 'Cancels the request.' },
|
|
7541
7542
|
{ name: 'options.inboxToken', type: 'string', required: false, description: 'The `oe_inbox_` token `create` returned. Overrides the token given to `createTempMail` for this call, and is required on the `OpenEmail` client, which would otherwise send its API key.' }
|
|
7542
7543
|
],
|
|
7543
7544
|
returns: '`DeletedTempMessageResource` with `object` set to `temp_message`, the message `id` and `deleted: true`.',
|
|
7544
7545
|
example: 'const temp = createTempMail()\n\nconst inbox = await temp.create()\n\nconst { items } = await temp.listMessages(inbox.id, { inboxToken: inbox.token })\n\nfor (const message of items.filter((row) => row.spam)) {\n await temp.deleteMessage(inbox.id, message.id, { inboxToken: inbox.token })\n}',
|
|
7545
7546
|
notes: [
|
|
7546
|
-
'An unknown or already deleted message id is 404 `resource_not_found`.',
|
|
7547
|
+
'An unknown or already deleted message id is 404 `resource_not_found`, and an install with no key to read the pool answers 503 `not_configured`.',
|
|
7547
7548
|
'Not retried automatically. If you repeat it yourself after a lost response, that 404 means the first attempt already worked.'
|
|
7548
7549
|
]
|
|
7549
7550
|
};
|
|
@@ -7554,14 +7555,14 @@ const TempMailDeleteMethod = {
|
|
|
7554
7555
|
accessor: 'openemail.tempMail.delete',
|
|
7555
7556
|
slug: 'temp-mail/delete',
|
|
7556
7557
|
signature: 'delete(inboxId: string, options?: InboxScope): Promise<DeletedTempInboxResource>',
|
|
7557
|
-
summary: '
|
|
7558
|
-
description: '
|
|
7558
|
+
summary: 'Move the mail in a disposable inbox to the bin now',
|
|
7559
|
+
description: 'Moves every message the inbox shows to the bin of the mailbox that runs the pool, at once. It does not end the lease: nothing about a lease is stored, so there is nothing to revoke, and the token keeps opening the address until its expiry. Mail that arrives afterwards is listed as usual.\n\nNothing holds the address back either, so it can be issued again at once, to anybody. An install with no key to read the pool answers 503 `not_configured`.\n\nThe response is a tombstone rather than an empty body, so a log line can name what went.',
|
|
7559
7560
|
method: 'DELETE',
|
|
7560
7561
|
path: '/temp-mail/inboxes/{id}',
|
|
7561
7562
|
scopes: [],
|
|
7562
7563
|
auth: 'inboxToken',
|
|
7563
7564
|
parameters: [
|
|
7564
|
-
{ name: 'inboxId', type: 'string', required: true, description: 'The `tinb_` id `create` returned. It
|
|
7565
|
+
{ name: 'inboxId', type: 'string', required: true, description: 'The `tinb_` id `create` returned. It is not checked against the token, which alone decides the inbox.' },
|
|
7565
7566
|
{ name: 'options.signal', type: 'AbortSignal', required: false, description: 'Cancels the request.' },
|
|
7566
7567
|
{ name: 'options.inboxToken', type: 'string', required: false, description: 'The `oe_inbox_` token `create` returned. Overrides the token given to `createTempMail` for this call, and is required on the `OpenEmail` client, which would otherwise send its API key.' }
|
|
7567
7568
|
],
|
|
@@ -7569,7 +7570,7 @@ const TempMailDeleteMethod = {
|
|
|
7569
7570
|
example: 'const temp = createTempMail()\n\nconst inbox = await temp.create()\n\nconst result = await temp.delete(inbox.id, { inboxToken: inbox.token })\n\nconsole.log(result.destroyed)',
|
|
7570
7571
|
notes: [
|
|
7571
7572
|
'The flag is `destroyed`, not `deleted` as on other tombstones.',
|
|
7572
|
-
'Not retried automatically.
|
|
7573
|
+
'Not retried automatically. Repeating it is harmless: the lease still stands, so a second call answers 200 and moves whatever has arrived since.'
|
|
7573
7574
|
]
|
|
7574
7575
|
};
|
|
7575
7576
|
|
|
@@ -7578,23 +7579,23 @@ const TempMailExtendMethod = {
|
|
|
7578
7579
|
name: 'extend',
|
|
7579
7580
|
accessor: 'openemail.tempMail.extend',
|
|
7580
7581
|
slug: 'temp-mail/extend',
|
|
7581
|
-
signature: 'extend(inboxId: string, options?: InboxScope): Promise<
|
|
7582
|
+
signature: 'extend(inboxId: string, options?: InboxScope): Promise<ExtendedTempInboxResource>',
|
|
7582
7583
|
summary: 'Push the expiry of an inbox an hour further out',
|
|
7583
|
-
description: 'Adds up to 60 minutes to `expiresAt` and resolves with the updated inbox. There is no body
|
|
7584
|
+
description: 'Adds up to 60 minutes to `expiresAt` and resolves with the updated inbox and a new `token` that carries the later expiry. There is no body. The old token keeps its old expiry, so use the new one from here on, including in a client built with `createTempMail({ inboxToken })`.\n\nThe new expiry is the earlier of one hour past the current expiry and 24 hours after `createdAt`, and a lease allows at most 23 extensions. The last extension can buy less than an hour, and on a lease that already reaches the 24 hours a call still succeeds, spends an extension and buys nothing. `extensionsLeft` counts only the 23 calls, so compare `expiresAt` with `createdAt` before offering more time.\n\nWhen `extensionsLeft` is 0 this answers 422 `extension_limit` for good, and the only way on is a new inbox. The response reads no mail, so its `messageCount` is 0 and its `lastMessageAt` is null.',
|
|
7584
7585
|
method: 'POST',
|
|
7585
7586
|
path: '/temp-mail/inboxes/{id}/extend',
|
|
7586
7587
|
scopes: [],
|
|
7587
7588
|
auth: 'inboxToken',
|
|
7588
7589
|
parameters: [
|
|
7589
|
-
{ name: 'inboxId', type: 'string', required: true, description: 'The `tinb_` id `create` returned. It
|
|
7590
|
+
{ name: 'inboxId', type: 'string', required: true, description: 'The `tinb_` id `create` returned. It is not checked against the token, which alone decides the inbox.' },
|
|
7590
7591
|
{ name: 'options.signal', type: 'AbortSignal', required: false, description: 'Cancels the request.' },
|
|
7591
7592
|
{ name: 'options.inboxToken', type: 'string', required: false, description: 'The `oe_inbox_` token `create` returned. Overrides the token given to `createTempMail` for this call, and is required on the `OpenEmail` client, which would otherwise send its API key.' }
|
|
7592
7593
|
],
|
|
7593
|
-
returns: '`TempInboxResource` with the new `expiresAt` and the updated `extensionsLeft`.',
|
|
7594
|
-
example: 'const temp = createTempMail({ inboxToken: \'oe_inbox_Vb3kT9qLm2Xw7RzN4pYc6HfJ1sGa5Ed8KuQo0iWnS2e\' })\n\nconst inbox = await temp.extend(\'
|
|
7594
|
+
returns: '`ExtendedTempInboxResource`: the `TempInboxResource` fields with the new `expiresAt` and the updated `extensionsLeft`, plus the new `token` beginning `oe_inbox_`.',
|
|
7595
|
+
example: 'const temp = createTempMail({ inboxToken: \'oe_inbox_Vb3kT9qLm2Xw7RzN4pYc6HfJ1sGa5Ed8KuQo0iWnS2e\' })\n\nconst inbox = await temp.extend(\'tinb_k7m2q9xw4bdp\')\n\nconsole.log(inbox.expiresAt, inbox.extensionsLeft)\n\nconst renewed = createTempMail({ inboxToken: inbox.token })',
|
|
7595
7596
|
notes: [
|
|
7596
7597
|
'Not retried automatically, because a replay would spend a second extension.',
|
|
7597
|
-
'An inbox created with `ttlMinutes: 1440`
|
|
7598
|
+
'An inbox created with `ttlMinutes: 1440` still reports `extensionsLeft: 23`, and none of them can add a minute.'
|
|
7598
7599
|
]
|
|
7599
7600
|
};
|
|
7600
7601
|
|
|
@@ -7605,21 +7606,21 @@ const TempMailGetMessageMethod = {
|
|
|
7605
7606
|
slug: 'temp-mail/getMessage',
|
|
7606
7607
|
signature: 'getMessage(inboxId: string, messageId: string, options?: InboxScope): Promise<TempMessageDetailResource>',
|
|
7607
7608
|
summary: 'Read one message with its stored body',
|
|
7608
|
-
description: 'Resolves the list row for one message plus the parsed message as stored. Reading
|
|
7609
|
+
description: 'Resolves the list row for one message plus the parsed message as stored. Reading it does not mark it seen: `seen` mirrors the unread state of the message in the mailbox that runs the pool, and nothing on these routes changes it.\n\nRender `message.decodedBody`. `body` and `processedHtml` are empty strings for every message that can reach a disposable inbox, so a client reading either shows a blank page. The body is never cut, so `truncated` is always false.\n\nThe HTML came from a stranger to an address anyone could name. Render it outside your own origin, for example in a sandboxed iframe.',
|
|
7609
7610
|
method: 'GET',
|
|
7610
7611
|
path: '/temp-mail/inboxes/{id}/messages/{messageId}',
|
|
7611
7612
|
scopes: [],
|
|
7612
7613
|
auth: 'inboxToken',
|
|
7613
7614
|
parameters: [
|
|
7614
|
-
{ name: 'inboxId', type: 'string', required: true, description: 'The `tinb_` id `create` returned. It
|
|
7615
|
-
{ name: 'messageId', type: 'string', required: true, description: '
|
|
7615
|
+
{ name: 'inboxId', type: 'string', required: true, description: 'The `tinb_` id `create` returned. It is not checked against the token, which alone decides the inbox.' },
|
|
7616
|
+
{ name: 'messageId', type: 'string', required: true, description: 'An `id` from `listMessages`, such as `thr_` and 24 hex.' },
|
|
7616
7617
|
{ name: 'options.signal', type: 'AbortSignal', required: false, description: 'Cancels the request.' },
|
|
7617
7618
|
{ name: 'options.inboxToken', type: 'string', required: false, description: 'The `oe_inbox_` token `create` returned. Overrides the token given to `createTempMail` for this call, and is required on the `OpenEmail` client, which would otherwise send its API key.' }
|
|
7618
7619
|
],
|
|
7619
7620
|
returns: '`TempMessageDetailResource`: every `TempMessageResource` field plus `message`, the stored parsed message, and `truncated`.',
|
|
7620
|
-
example: 'const temp = createTempMail({ inboxToken: \'oe_inbox_Vb3kT9qLm2Xw7RzN4pYc6HfJ1sGa5Ed8KuQo0iWnS2e\' })\n\nconst detail = await temp.getMessage(\'
|
|
7621
|
+
example: 'const temp = createTempMail({ inboxToken: \'oe_inbox_Vb3kT9qLm2Xw7RzN4pYc6HfJ1sGa5Ed8KuQo0iWnS2e\' })\n\nconst detail = await temp.getMessage(\'tinb_k7m2q9xw4bdp\', \'thr_9e3b7c1a5f2d8e40b6a9c3f1\')\n\nconsole.log(detail.subject, detail.truncated)\nconsole.log(detail.message.decodedBody)',
|
|
7621
7622
|
notes: [
|
|
7622
|
-
'
|
|
7623
|
+
'A message this lease cannot see, deleted ones included, is 404 `resource_not_found`. An expired lease is 401 `inbox_expired`, and an install with no key to read the pool answers 503 `not_configured`.',
|
|
7623
7624
|
'`message` is typed as a loose record, so narrow `decodedBody` to a string before rendering it.'
|
|
7624
7625
|
]
|
|
7625
7626
|
};
|
|
@@ -7631,20 +7632,20 @@ const TempMailGetMethod = {
|
|
|
7631
7632
|
slug: 'temp-mail/get',
|
|
7632
7633
|
signature: 'get(inboxId: string, options?: InboxScope): Promise<TempInboxResource>',
|
|
7633
7634
|
summary: 'Read the lease and counters of a disposable inbox',
|
|
7634
|
-
description: 'Resolves the inbox with its expiry, remaining extensions and message counters, without any messages. To watch an inbox, poll `listMessages` instead: it returns `expiresAt` alongside the mail, so one request covers both.\n\nThis call is authorised by the inbox token, never by an API key. A workspace key sent in its place is refused with 401 `
|
|
7635
|
+
description: 'Resolves the inbox with its expiry, remaining extensions and message counters, without any messages. To watch an inbox, poll `listMessages` instead: it returns `expiresAt` alongside the mail, so one request covers both.\n\nThis call is authorised by the inbox token, never by an API key. A workspace key sent in its place is refused with 401 `missing_inbox_token`, and no scope on any key reaches this resource. The token alone decides which inbox is read, and the id in the path is not checked against it.\n\nOnce the lease is over the token answers 401 `inbox_expired`, and a token this server did not sign answers 404 `resource_not_found`. Deleting an inbox does not end its lease, so its token still reads it. `messageCount` is counted by reading every page, and is 0 when the install cannot read the mailbox that runs the pool.',
|
|
7635
7636
|
method: 'GET',
|
|
7636
7637
|
path: '/temp-mail/inboxes/{id}',
|
|
7637
7638
|
scopes: [],
|
|
7638
7639
|
auth: 'inboxToken',
|
|
7639
7640
|
parameters: [
|
|
7640
|
-
{ name: 'inboxId', type: 'string', required: true, description: 'The `tinb_` id `create` returned. It
|
|
7641
|
+
{ name: 'inboxId', type: 'string', required: true, description: 'The `tinb_` id `create` returned. It is not checked against the token, which alone decides the inbox.' },
|
|
7641
7642
|
{ name: 'options.signal', type: 'AbortSignal', required: false, description: 'Cancels the request.' },
|
|
7642
7643
|
{ name: 'options.inboxToken', type: 'string', required: false, description: 'The `oe_inbox_` token `create` returned. Overrides the token given to `createTempMail` for this call, and is required on the `OpenEmail` client, which would otherwise send its API key.' }
|
|
7643
7644
|
],
|
|
7644
7645
|
returns: '`TempInboxResource` with `id`, `address`, `domain`, `createdAt`, `expiresAt`, `extensionsLeft`, `messageCount`, `messageLimit` and `lastMessageAt`.',
|
|
7645
7646
|
example: 'const temp = createTempMail()\n\nconst created = await temp.create()\n\nconst inbox = await temp.get(created.id, { inboxToken: created.token })\n\nconsole.log(inbox.expiresAt, inbox.messageCount, inbox.messageLimit)',
|
|
7646
7647
|
notes: [
|
|
7647
|
-
'A token that is
|
|
7648
|
+
'A token that does not start with `oe_inbox_` is 401 `missing_inbox_token`, the same as no token at all. One that starts with it but was not signed by this server is 404 `resource_not_found`.',
|
|
7648
7649
|
'`messageCount` counts every message the inbox is showing, across every page, and goes down when one is deleted. `messageLimit`, which is 50, is the page size of `listMessages`, not a ceiling: nothing past it is dropped.'
|
|
7649
7650
|
]
|
|
7650
7651
|
};
|
|
@@ -7708,23 +7709,23 @@ const TempMailListAttachmentsMethod = {
|
|
|
7708
7709
|
name: 'listAttachments',
|
|
7709
7710
|
accessor: 'openemail.tempMail.listAttachments',
|
|
7710
7711
|
slug: 'temp-mail/listAttachments',
|
|
7711
|
-
signature: 'listAttachments(inboxId: string, messageId: string, options?: InboxScope): Promise<Array<
|
|
7712
|
-
summary: '
|
|
7713
|
-
description: 'Resolves every attachment on a message as a plain array
|
|
7712
|
+
signature: 'listAttachments(inboxId: string, messageId: string, options?: InboxScope): Promise<Array<TempAttachmentResource>>',
|
|
7713
|
+
summary: 'List the attachments of a message, metadata only',
|
|
7714
|
+
description: 'Resolves every attachment on a message as a plain array of metadata: `attachmentId`, `filename`, `mimeType` and `size` in decoded bytes, with `body` an empty string and `headers` empty. No route on a disposable inbox serves the bytes, and there is no per attachment fetch.\n\n`filename` and `mimeType` are whatever the sender declared, and nothing here is scanned. A message this lease cannot see is 404 `resource_not_found`, and an install with no key to read the pool answers 503 `not_configured`.',
|
|
7714
7715
|
method: 'GET',
|
|
7715
7716
|
path: '/temp-mail/inboxes/{id}/messages/{messageId}/attachments',
|
|
7716
7717
|
scopes: [],
|
|
7717
7718
|
auth: 'inboxToken',
|
|
7718
7719
|
parameters: [
|
|
7719
|
-
{ name: 'inboxId', type: 'string', required: true, description: 'The `tinb_` id `create` returned. It
|
|
7720
|
-
{ name: 'messageId', type: 'string', required: true, description: '
|
|
7720
|
+
{ name: 'inboxId', type: 'string', required: true, description: 'The `tinb_` id `create` returned. It is not checked against the token, which alone decides the inbox.' },
|
|
7721
|
+
{ name: 'messageId', type: 'string', required: true, description: 'An `id` from `listMessages`, such as `thr_` and 24 hex.' },
|
|
7721
7722
|
{ name: 'options.signal', type: 'AbortSignal', required: false, description: 'Cancels the request.' },
|
|
7722
7723
|
{ name: 'options.inboxToken', type: 'string', required: false, description: 'The `oe_inbox_` token `create` returned. Overrides the token given to `createTempMail` for this call, and is required on the `OpenEmail` client, which would otherwise send its API key.' }
|
|
7723
7724
|
],
|
|
7724
|
-
returns: '`Array<
|
|
7725
|
-
example: 'const temp = createTempMail({ inboxToken: \'oe_inbox_Vb3kT9qLm2Xw7RzN4pYc6HfJ1sGa5Ed8KuQo0iWnS2e\' })\n\nconst attachments = await temp.listAttachments(\'
|
|
7725
|
+
returns: '`Array<TempAttachmentResource>`, each with `attachmentId`, `filename`, `mimeType`, `size` in decoded bytes, `body` as an empty string and `headers` as an empty array.',
|
|
7726
|
+
example: 'const temp = createTempMail({ inboxToken: \'oe_inbox_Vb3kT9qLm2Xw7RzN4pYc6HfJ1sGa5Ed8KuQo0iWnS2e\' })\n\nconst attachments = await temp.listAttachments(\'tinb_k7m2q9xw4bdp\', \'thr_9e3b7c1a5f2d8e40b6a9c3f1\')\n\nfor (const file of attachments) console.log(file.filename, file.size)',
|
|
7726
7727
|
notes: [
|
|
7727
|
-
'
|
|
7728
|
+
'Listing attachments does not mark the message seen.',
|
|
7728
7729
|
'Check `attachmentCount` from `listMessages` first to skip this call for messages with none.'
|
|
7729
7730
|
]
|
|
7730
7731
|
};
|
|
@@ -7736,7 +7737,7 @@ const TempMailListDomainsMethod = {
|
|
|
7736
7737
|
slug: 'temp-mail/listDomains',
|
|
7737
7738
|
signature: 'listDomains(options?: InboxScope): Promise<Array<TempDomainResource>>',
|
|
7738
7739
|
summary: 'List the domains a disposable inbox can be created on',
|
|
7739
|
-
description: 'Resolves the pool of domains `create` accepts, as a plain array in the order
|
|
7740
|
+
description: 'Resolves the pool of domains `create` accepts, as a plain array in the order the operator wrote them in `TEMP_MAIL_DOMAINS`. It takes no credential at all: no API key and no inbox token are sent, even when the client holding this namespace has one.\n\nNothing checks that a listed domain is verified, so the list is only as good as the operator made it. An empty array is a normal answer meaning this install offers no disposable domains, and it is exactly the condition under which `create` fails with 503 `not_configured`.',
|
|
7740
7741
|
method: 'GET',
|
|
7741
7742
|
path: '/temp-mail/domains',
|
|
7742
7743
|
scopes: [],
|
|
@@ -7748,7 +7749,7 @@ const TempMailListDomainsMethod = {
|
|
|
7748
7749
|
returns: '`Array<TempDomainResource>`, each with `object` set to `temp_domain` and a lowercased `domain` to pass back to `create`.',
|
|
7749
7750
|
example: 'const temp = createTempMail()\n\nconst domains = await temp.listDomains()\n\nconsole.log(domains.map((entry) => entry.domain))',
|
|
7750
7751
|
notes: [
|
|
7751
|
-
'The
|
|
7752
|
+
'The pool is server configuration, so a domain appears when the operator adds it to `TEMP_MAIL_DOMAINS`, not when it is added to a workspace.',
|
|
7752
7753
|
'Retried automatically on network failure and retryable statuses, like every GET.'
|
|
7753
7754
|
]
|
|
7754
7755
|
};
|
|
@@ -7760,13 +7761,13 @@ const TempMailListMessagesMethod = {
|
|
|
7760
7761
|
slug: 'temp-mail/listMessages',
|
|
7761
7762
|
signature: 'listMessages(inboxId: string, options?: TempMessageListOptions): Promise<TempMessagesResource>',
|
|
7762
7763
|
summary: 'List one page of the messages in a disposable inbox',
|
|
7763
|
-
description: 'Resolves one page of the messages in the inbox newest first, by the time the server received them, together with the inbox `expiresAt`. Rows carry metadata only, and
|
|
7764
|
+
description: 'Resolves one page of the messages in the inbox newest first, by the time the server received them, together with the inbox `expiresAt`. Rows carry metadata only, and polling this is the way to wait for a confirmation email. Only mail delivered to this address since the lease began is listed.\n\nA page holds up to 50 messages. When more have arrived, `hasMore` is true and `nextCursor` goes back as `options.cursor` for the next page, so nothing that reached the inbox is hidden; `listAllMessages` and `iterateMessages` do that walk for you. A page can hold fewer than `limit` rows, even none, while `hasMore` is true, because mail to other addresses on the pool is read and dropped. `snippet` is plain text capped at 400 characters, which is often enough to read a one time code without opening the message.\n\n`spam` is a flag, never a filing decision. A machine sent confirmation from a sender with no reputation is exactly what a disposable inbox exists to receive, so flagged messages are still listed. `from` is whatever the message claimed and has not been authenticated.',
|
|
7764
7765
|
method: 'GET',
|
|
7765
7766
|
path: '/temp-mail/inboxes/{id}/messages',
|
|
7766
7767
|
scopes: [],
|
|
7767
7768
|
auth: 'inboxToken',
|
|
7768
7769
|
parameters: [
|
|
7769
|
-
{ name: 'inboxId', type: 'string', required: true, description: 'The `tinb_` id `create` returned. It
|
|
7770
|
+
{ name: 'inboxId', type: 'string', required: true, description: 'The `tinb_` id `create` returned. It is not checked against the token, which alone decides the inbox.' },
|
|
7770
7771
|
{ name: 'options.limit', type: 'number', required: false, description: 'Messages per page, a whole number from 1 to 50, defaulting to 50. Out of range is 422 `invalid_parameter`.' },
|
|
7771
7772
|
{ name: 'options.cursor', type: 'string', required: false, description: 'The `nextCursor` from the previous page. Never build one yourself.' },
|
|
7772
7773
|
{ name: 'options.signal', type: 'AbortSignal', required: false, description: 'Cancels the request.' },
|
|
@@ -7778,7 +7779,8 @@ const TempMailListMessagesMethod = {
|
|
|
7778
7779
|
notes: [
|
|
7779
7780
|
'A message whose `Message-ID` header matches one already in the inbox is not stored twice.',
|
|
7780
7781
|
'`to` is the inbox address. A `+tag` the sender added is folded back into the base address.',
|
|
7781
|
-
'Every message the inbox has received is listed, a page at a time. `messageLimit` on the inbox is the size the tool is built for, not a point past which mail is hidden.'
|
|
7782
|
+
'Every message the inbox has received is listed, a page at a time. `messageLimit` on the inbox is the size the tool is built for, not a point past which mail is hidden.',
|
|
7783
|
+
'An install with no key to read the pool answers 503 `not_configured`.'
|
|
7782
7784
|
]
|
|
7783
7785
|
};
|
|
7784
7786
|
|
|
@@ -13661,6 +13663,7 @@ const RESOURCE_ONE_TIME_SECRETS = {
|
|
|
13661
13663
|
const RESOURCE_TEMP_INBOX = {
|
|
13662
13664
|
NAMESPACE: 'tempMail',
|
|
13663
13665
|
CREATE: 'create',
|
|
13666
|
+
EXTEND: 'extend',
|
|
13664
13667
|
DELETE: 'delete',
|
|
13665
13668
|
TOKEN_FIELD: 'token'
|
|
13666
13669
|
};
|
|
@@ -13818,6 +13821,12 @@ const RESOURCE_TYPE_COLUMNS = {
|
|
|
13818
13821
|
TempMessagesResource: TEMP_MESSAGE_COLUMNS,
|
|
13819
13822
|
TempMessageResource: TEMP_MESSAGE_COLUMNS,
|
|
13820
13823
|
TempDomainResource: [Column('domain', 'domain')],
|
|
13824
|
+
TempAttachmentResource: [
|
|
13825
|
+
Column('id', 'attachmentId', RENDER_CELL_FORMATS.ID),
|
|
13826
|
+
Column('filename', 'filename'),
|
|
13827
|
+
Column('type', 'mimeType'),
|
|
13828
|
+
Column('size', 'size', RENDER_CELL_FORMATS.BYTES)
|
|
13829
|
+
],
|
|
13821
13830
|
ApiKeyRequestResource: [
|
|
13822
13831
|
Column('id', 'id', RENDER_CELL_FORMATS.ID),
|
|
13823
13832
|
Column('method', 'method'),
|
|
@@ -13921,6 +13930,7 @@ const RESOURCE_ID_LIST_KEY = 'ids';
|
|
|
13921
13930
|
const RESOURCE_ID_LIST_SUFFIX = 'Ids';
|
|
13922
13931
|
const RESOURCE_MAX_SAMPLE = '100';
|
|
13923
13932
|
const RESOURCE_READ_METHOD = 'GET';
|
|
13933
|
+
const RESOURCE_PATCH_METHOD = 'PATCH';
|
|
13924
13934
|
const RESOURCE_EXAMPLE_LIMIT = 5;
|
|
13925
13935
|
const RESOURCE_EXAMPLE_OPTIONAL_FLAGS = 3;
|
|
13926
13936
|
const RESOURCE_CONFIRM_TARGET_LIMIT = 5;
|
|
@@ -13941,6 +13951,7 @@ const RESOURCE_UNSUPPORTED_ESCAPE = /\\[^'\\]/;
|
|
|
13941
13951
|
const RESOURCE_SECRET_FLAG = /(key|token|secret|password)$/;
|
|
13942
13952
|
const RESOURCE_SENTENCE_BREAK = /(?<=[.!?])\s+(?=[A-Z`])/;
|
|
13943
13953
|
const RESOURCE_CAMEL_PATH = /^[a-z]+[A-Z][A-Za-z]*$/;
|
|
13954
|
+
const RESOURCE_EMPTY_STRING_CLEAR = /(\b[Nn]ull\b|`null`) or an empty string/g;
|
|
13944
13955
|
const RESOURCE_EXAMPLE_PATTERNS = {
|
|
13945
13956
|
STRING: /(\w+):\s*'((?:[^'\\\n]|\\.)*)'/g,
|
|
13946
13957
|
NUMBER: /(\w+):\s*(-?\d+(?:\.\d+)?)(?=\s*[,}\n])/g,
|
|
@@ -13975,10 +13986,11 @@ const RESOURCE_HELP = {
|
|
|
13975
13986
|
NOTES: 'NOTES',
|
|
13976
13987
|
NO_SCOPES: 'Any key or sign-in for the workspace.',
|
|
13977
13988
|
SIGNED_OUT: 'None. It works signed out, with no key.',
|
|
13978
|
-
INBOX_TOKEN: 'None. The inbox token authorises it: `--inbox-token`, or the token the CLI saved when it created the inbox.',
|
|
13989
|
+
INBOX_TOKEN: 'None. The inbox token authorises it: `--inbox-token`, or the token the CLI saved when it created or last extended the inbox.',
|
|
13979
13990
|
REQUIRED: 'Required.',
|
|
13980
13991
|
REQUIRED_WITH_DATA: 'Required, here or in `--data`.',
|
|
13981
13992
|
PAGING: 'Add `--all` to walk every page: a table on a terminal, one JSON object per line when piped or with `--ndjson`, and one `{ items, hasMore, nextCursor }` document with `--json`. `--max <n>` stops after that many items.',
|
|
13993
|
+
PAGING_OWN_SHAPE: 'Add `--all` to walk every page: a table on a terminal, one JSON object per line when piped or with `--ndjson`, and one JSON document with `--json`, shaped as Returns describes. `--max <n>` stops after that many items.',
|
|
13982
13994
|
DATA_USAGE: '--data <json|@file|->',
|
|
13983
13995
|
JSON_TYPE: (type) => `JSON shaped as \`${type}\`, inline or from a file with @path.`
|
|
13984
13996
|
};
|
|
@@ -14004,6 +14016,7 @@ const RESOURCE_MESSAGES = {
|
|
|
14004
14016
|
INBOX_TOKEN_REJECTED: 'The inbox token was refused.',
|
|
14005
14017
|
INBOX_TOKEN_REJECTED_NEXT: 'Check `--inbox-token`, or create a new inbox with `openemail temp new`.',
|
|
14006
14018
|
INBOX_SAVED: (address) => `Saved the inbox token for ${address}, so later temp-mail commands find it.`,
|
|
14019
|
+
INBOX_RENEWED: (address) => `Saved the new inbox token for ${address}, which carries the later expiry, so later temp-mail commands use it.`,
|
|
14007
14020
|
SECRET_IN_ARGV: (label) => `${label} was passed on the command line, where your shell history and the process list can show it. Next time pass \`-\` to read it from standard input, or \`@path\` to read it from a file.`,
|
|
14008
14021
|
NO_KEYLESS_RESOURCE: (namespace) => `The ${namespace} commands need a credential.`,
|
|
14009
14022
|
NO_METHOD: (accessor) => `${accessor} is not in the SDK this CLI was built with.`,
|
|
@@ -14104,13 +14117,13 @@ const TEMP_FLAGS = {
|
|
|
14104
14117
|
name: TEMP_FLAG_NAMES.DOMAIN,
|
|
14105
14118
|
kind: FLAG_KINDS.STRING,
|
|
14106
14119
|
placeholder: 'domain',
|
|
14107
|
-
description: 'Domain from `openemail temp-mail list-domains`. Left out, one
|
|
14120
|
+
description: 'Domain from `openemail temp-mail list-domains`. Left out, the first one in that list is used'
|
|
14108
14121
|
},
|
|
14109
14122
|
NAME: {
|
|
14110
14123
|
name: TEMP_FLAG_NAMES.NAME,
|
|
14111
14124
|
kind: FLAG_KINDS.STRING,
|
|
14112
14125
|
placeholder: 'local-part',
|
|
14113
|
-
description: 'The part before the @,
|
|
14126
|
+
description: 'The part before the @, up to 64 letters, digits, dots, dashes or underscores, starting and ending with a letter or digit. Left out, one is generated. Anyone who asks for the same name reads its mail too'
|
|
14114
14127
|
},
|
|
14115
14128
|
TTL: {
|
|
14116
14129
|
name: TEMP_FLAG_NAMES.TTL,
|
|
@@ -14142,7 +14155,10 @@ const TEMP_LIMITS = {
|
|
|
14142
14155
|
SNIPPET_WIDTH: 60
|
|
14143
14156
|
};
|
|
14144
14157
|
const TEMP_TOKEN_PREFIX = 'oe_inbox_';
|
|
14145
|
-
const TEMP_MESSAGE_PREFIX = '
|
|
14158
|
+
const TEMP_MESSAGE_PREFIX = 'thr_';
|
|
14159
|
+
const TEMP_API_ERROR_CODES = {
|
|
14160
|
+
INBOX_EXPIRED: 'inbox_expired'
|
|
14161
|
+
};
|
|
14146
14162
|
const TEMP_COLUMNS = {
|
|
14147
14163
|
ID: 'Inbox',
|
|
14148
14164
|
ADDRESS: 'Address',
|
|
@@ -14157,7 +14173,7 @@ const TEMP_MESSAGES = {
|
|
|
14157
14173
|
CREATING: 'Creating a temporary inbox',
|
|
14158
14174
|
LOADING: 'Loading messages',
|
|
14159
14175
|
LOADING_MESSAGE: 'Loading the message',
|
|
14160
|
-
DELETING: '
|
|
14176
|
+
DELETING: 'Moving the mail to the bin',
|
|
14161
14177
|
NO_INBOXES: 'You have no temporary inboxes.',
|
|
14162
14178
|
NO_INBOXES_NEXT: 'Run `openemail temp new` to create one.',
|
|
14163
14179
|
PICK_INBOX: 'Which inbox?',
|
|
@@ -14166,12 +14182,9 @@ const TEMP_MESSAGES = {
|
|
|
14166
14182
|
TTL_RANGE: '--ttl takes a whole number of minutes from 1 to 1440.',
|
|
14167
14183
|
INVALID_TOKEN: '--inbox-token must be an inbox token beginning oe_inbox_.',
|
|
14168
14184
|
NO_MESSAGES: 'No mail yet.',
|
|
14169
|
-
WATCH_STOPPED: 'Stopped watching.',
|
|
14170
14185
|
EXPIRED: 'The inbox has expired, so there is nothing more to watch.',
|
|
14171
|
-
GONE_LOCALLY: 'The
|
|
14172
|
-
|
|
14173
|
-
EMPTY_BODY: '(This message has no body.)',
|
|
14174
|
-
UNVERIFIED_SENDER: 'The sender of mail in a temporary inbox is not verified.',
|
|
14186
|
+
GONE_LOCALLY: 'The server no longer accepts this inbox token, so nothing was moved and the inbox was only forgotten here.',
|
|
14187
|
+
LEASE_RUNS_ON: 'The lease runs on until it expires, so mail can still arrive, and nothing holds the address back from anybody else.',
|
|
14175
14188
|
MORE: (id) => `Older messages are not shown here. Run \`openemail temp-mail list-messages ${id} --all\` to see every one.`,
|
|
14176
14189
|
ALREADY_HERE: 'Already in the inbox:',
|
|
14177
14190
|
CREATED: (address) => `Created ${address}.`,
|
|
@@ -14181,8 +14194,8 @@ const TEMP_MESSAGES = {
|
|
|
14181
14194
|
PRUNED: (count) => `Forgot ${count} expired ${count === 1 ? 'inbox' : 'inboxes'}.`,
|
|
14182
14195
|
WATCHING: (address) => `Watching ${address}`,
|
|
14183
14196
|
WAITING: (left) => `Waiting for mail, ${left} left. Press Ctrl+C to stop`,
|
|
14184
|
-
CONFIRM_DELETE: (address) => `
|
|
14185
|
-
DELETED: (address) => `
|
|
14197
|
+
CONFIRM_DELETE: (address) => `Move every message in ${address} to the bin and forget the inbox here?`,
|
|
14198
|
+
DELETED: (address) => `Moved the mail in ${address} to the bin and forgot the inbox here.`,
|
|
14186
14199
|
SENDER_HINT: (id) => `Run \`openemail temp read ${id} <message-id>\` to read one.`
|
|
14187
14200
|
};
|
|
14188
14201
|
|
|
@@ -14268,6 +14281,7 @@ const TERMINAL = {
|
|
|
14268
14281
|
MIN_WRAP_WIDTH: 16
|
|
14269
14282
|
};
|
|
14270
14283
|
const CODE_SPAN_DELIMITER = '`';
|
|
14284
|
+
const CODE_SPAN_PATTERN = /`([^`\s](?:[^`]*[^`\s])?)`/g;
|
|
14271
14285
|
const BANNER_LINES = [
|
|
14272
14286
|
' _ _',
|
|
14273
14287
|
' ___ _ __ ___ _ _ ___ _ __ __ _ (_)| |',
|
|
@@ -15535,13 +15549,7 @@ const Config$1 = {
|
|
|
15535
15549
|
Update: Update$1
|
|
15536
15550
|
};
|
|
15537
15551
|
|
|
15538
|
-
const FormatCode = (text, paint) =>
|
|
15539
|
-
const parts = text.split(CODE_SPAN_DELIMITER);
|
|
15540
|
-
if (parts.length < 3)
|
|
15541
|
-
return text;
|
|
15542
|
-
const balanced = parts.length % 2 === 1 ? parts : [...parts.slice(0, -2), `${parts[parts.length - 2]}${CODE_SPAN_DELIMITER}${parts[parts.length - 1]}`];
|
|
15543
|
-
return balanced.map((part, index) => index % 2 === 1 ? paint(part) : part).join('');
|
|
15544
|
-
};
|
|
15552
|
+
const FormatCode = (text, paint) => text.replace(CODE_SPAN_PATTERN, (_span, code) => paint(code));
|
|
15545
15553
|
|
|
15546
15554
|
const KNOWN_KINDS = new Set(Object.values(PROFILE_KINDS));
|
|
15547
15555
|
const IsUsable = (value) => IsRecord$2(value) && KNOWN_KINDS.has(value.kind) && typeof value.baseUrl === 'string';
|
|
@@ -17622,6 +17630,17 @@ const NonEmptyEnv = (name) => {
|
|
|
17622
17630
|
return value === undefined || value.length === 0 ? null : value;
|
|
17623
17631
|
};
|
|
17624
17632
|
|
|
17633
|
+
const LOCAL_NETWORK = /^(localhost|127\.\d+\.\d+\.\d+|0\.0\.0\.0|\[::1\]|10\.\d+\.\d+\.\d+|192\.168\.\d+\.\d+|172\.(1[6-9]|2\d|3[01])\.\d+\.\d+|[^/:]+\.local)(:|\/|$)/i;
|
|
17634
|
+
|
|
17635
|
+
const withScheme = (value) => {
|
|
17636
|
+
const trimmed = value.trim();
|
|
17637
|
+
if (trimmed === '' || /^https?:\/\//i.test(trimmed))
|
|
17638
|
+
return trimmed;
|
|
17639
|
+
const bare = trimmed.replace(/^\/+/, '');
|
|
17640
|
+
return `${LOCAL_NETWORK.test(bare) ? 'http' : 'https'}://${bare}`;
|
|
17641
|
+
};
|
|
17642
|
+
const publicUrl = (value) => withScheme(value).replace(/\/+$/, '');
|
|
17643
|
+
|
|
17625
17644
|
const Invalid = (value) => new UsageError(MESSAGES.INVALID_BASE_URL(Color.Sanitize(value)), { code: CLI_ERROR_CODES.INVALID_VALUE });
|
|
17626
17645
|
const Parse$3 = (url) => {
|
|
17627
17646
|
try {
|
|
@@ -17641,8 +17660,8 @@ const Unspecified = (normalised, url) => new UsageError(MESSAGES.UNSPECIFIED_HOS
|
|
|
17641
17660
|
next: MESSAGES.UNSPECIFIED_HOST_NEXT(Color.Sanitize(LoopbackInstead(url)))
|
|
17642
17661
|
});
|
|
17643
17662
|
const NormaliseUrl = (value) => {
|
|
17644
|
-
const normalised =
|
|
17645
|
-
if (normalised ===
|
|
17663
|
+
const normalised = publicUrl(value);
|
|
17664
|
+
if (normalised.length === 0)
|
|
17646
17665
|
throw Invalid(value);
|
|
17647
17666
|
const url = Parse$3(normalised);
|
|
17648
17667
|
if (url === null || (url.protocol !== URL_PROTOCOLS.HTTP && url.protocol !== URL_PROTOCOLS.HTTPS))
|
|
@@ -18410,6 +18429,17 @@ const Find$1 = (idOrAddress) => {
|
|
|
18410
18429
|
return List$2().find(inbox => inbox.id === idOrAddress || inbox.address.toLowerCase() === needle) ?? null;
|
|
18411
18430
|
};
|
|
18412
18431
|
const Add = (inbox) => Change(inboxes => [...inboxes.filter(existing => existing.id !== inbox.id), inbox]);
|
|
18432
|
+
const Renew$1 = (id, inboxToken, expiresAt) => {
|
|
18433
|
+
const outcome = { renewed: false };
|
|
18434
|
+
Change(inboxes => {
|
|
18435
|
+
const stored = inboxes.find(inbox => inbox.id === id);
|
|
18436
|
+
if (stored === undefined || Date.parse(expiresAt) < Date.parse(stored.expiresAt))
|
|
18437
|
+
return null;
|
|
18438
|
+
outcome.renewed = true;
|
|
18439
|
+
return inboxes.map(inbox => inbox === stored ? { ...inbox, inboxToken, expiresAt } : inbox);
|
|
18440
|
+
});
|
|
18441
|
+
return outcome.renewed;
|
|
18442
|
+
};
|
|
18413
18443
|
const Remove$1 = (id) => {
|
|
18414
18444
|
const outcome = { removed: false };
|
|
18415
18445
|
Change(inboxes => {
|
|
@@ -18433,6 +18463,7 @@ const TempMailStore = {
|
|
|
18433
18463
|
List: List$2,
|
|
18434
18464
|
Find: Find$1,
|
|
18435
18465
|
Add,
|
|
18466
|
+
Renew: Renew$1,
|
|
18436
18467
|
Remove: Remove$1,
|
|
18437
18468
|
PruneExpired
|
|
18438
18469
|
};
|
|
@@ -19257,11 +19288,7 @@ const PathOf = (raw) => {
|
|
|
19257
19288
|
return raw;
|
|
19258
19289
|
}
|
|
19259
19290
|
};
|
|
19260
|
-
const WithoutDocsPrefix = (path) =>
|
|
19261
|
-
if (path === DOCS_PATHS.DOCS_PREFIX.slice(0, -1))
|
|
19262
|
-
return '';
|
|
19263
|
-
return path.startsWith(DOCS_PATHS.DOCS_PREFIX) ? path.slice(DOCS_PATHS.DOCS_PREFIX.length) : path;
|
|
19264
|
-
};
|
|
19291
|
+
const WithoutDocsPrefix = (path) => path.replace(DOCS_PATTERNS.DOCS_PREFIX, '');
|
|
19265
19292
|
const Decoded = (segment) => {
|
|
19266
19293
|
try {
|
|
19267
19294
|
return decodeURIComponent(segment);
|
|
@@ -25631,6 +25658,8 @@ const Send = {
|
|
|
25631
25658
|
}
|
|
25632
25659
|
};
|
|
25633
25660
|
|
|
25661
|
+
const InboxGone = (error) => error instanceof OpenEmailApiError && (error.isNotFound || error.code === TEMP_API_ERROR_CODES.INBOX_EXPIRED);
|
|
25662
|
+
|
|
25634
25663
|
const FromStored = (inbox) => ({
|
|
25635
25664
|
id: inbox.id,
|
|
25636
25665
|
address: inbox.address,
|
|
@@ -25697,15 +25726,15 @@ const TempClient = (baseUrl, signal) => createTempMail(SdkBaseOptions(baseUrl, s
|
|
|
25697
25726
|
const TempDelete = {
|
|
25698
25727
|
name: TEMP_COMMAND_NAMES.DELETE,
|
|
25699
25728
|
aliases: ['rm'],
|
|
25700
|
-
summary: '
|
|
25729
|
+
summary: 'Move the mail in a disposable inbox to the bin and forget its token',
|
|
25701
25730
|
usage: ['temp delete [inbox] [--yes]'],
|
|
25702
|
-
description: '
|
|
25731
|
+
description: 'Moves every message the inbox shows to the bin at once, then removes the inbox and its token from `~/.openemail/temp-mail.json`. Nothing about the lease is stored on the server, so there is nothing to revoke: it runs on until its expiry, and mail that arrives in the meantime still reaches whoever holds the token. Nothing holds the address back either, so it can be issued again at once, to anybody. You are asked to confirm unless you pass `--yes`.',
|
|
25703
25732
|
arguments: [{ name: 'inbox', description: 'Inbox id or address from `openemail temp list`, optional when you have only one' }],
|
|
25704
25733
|
flags: [TEMP_FLAGS.INBOX_TOKEN],
|
|
25705
25734
|
examples: [
|
|
25706
25735
|
{ command: 'openemail temp delete' },
|
|
25707
25736
|
{ command: 'openemail temp rm quiet-otter-12@example-temp.com --yes' },
|
|
25708
|
-
{ command: 'openemail temp delete
|
|
25737
|
+
{ command: 'openemail temp delete tinb_k7m2q9xw4bdp --yes --json' }
|
|
25709
25738
|
],
|
|
25710
25739
|
auth: COMMAND_AUTHS.NONE,
|
|
25711
25740
|
destructive: true,
|
|
@@ -25716,7 +25745,7 @@ const TempDelete = {
|
|
|
25716
25745
|
if (!await ConfirmDestructive(TEMP_MESSAGES.CONFIRM_DELETE(address)))
|
|
25717
25746
|
return EXIT_CODES.CANCELLED;
|
|
25718
25747
|
const gone = await CallAnonymous(context, TEMP_MESSAGES.DELETING, signal => TempClient(baseUrl, signal).delete(target.id, { inboxToken: target.token, signal })).then(() => false, (error) => {
|
|
25719
|
-
if (error
|
|
25748
|
+
if (InboxGone(error))
|
|
25720
25749
|
return true;
|
|
25721
25750
|
throw error;
|
|
25722
25751
|
});
|
|
@@ -25725,10 +25754,12 @@ const TempDelete = {
|
|
|
25725
25754
|
context.io.Json({ object: 'temp_inbox', id: target.id, address: target.address, destroyed: !gone, forgotten });
|
|
25726
25755
|
return EXIT_CODES.OK;
|
|
25727
25756
|
}
|
|
25728
|
-
if (gone)
|
|
25757
|
+
if (gone) {
|
|
25729
25758
|
context.io.Warn(TEMP_MESSAGES.GONE_LOCALLY);
|
|
25730
|
-
|
|
25731
|
-
|
|
25759
|
+
return EXIT_CODES.OK;
|
|
25760
|
+
}
|
|
25761
|
+
context.io.Success(TEMP_MESSAGES.DELETED(address));
|
|
25762
|
+
context.io.Hint(TEMP_MESSAGES.LEASE_RUNS_ON);
|
|
25732
25763
|
return EXIT_CODES.OK;
|
|
25733
25764
|
}
|
|
25734
25765
|
};
|
|
@@ -25737,7 +25768,7 @@ const TempList = {
|
|
|
25737
25768
|
name: TEMP_COMMAND_NAMES.LIST,
|
|
25738
25769
|
summary: 'List the disposable inboxes this CLI created',
|
|
25739
25770
|
usage: ['temp list'],
|
|
25740
|
-
description: 'Lists the inboxes kept in `~/.openemail/temp-mail.json`, soonest to expire last. Expired inboxes are forgotten as they are found, because
|
|
25771
|
+
description: 'Lists the inboxes kept in `~/.openemail/temp-mail.json`, soonest to expire last. Expired inboxes are forgotten as they are found, because their tokens no longer open anything. An inbox extended with `openemail temp-mail extend` shows its later expiry. Nothing is read from the network.',
|
|
25741
25772
|
examples: [
|
|
25742
25773
|
{ command: 'openemail temp list' },
|
|
25743
25774
|
{ command: 'openemail temp ls --json', note: 'Ids, addresses and expiry times, never the tokens' }
|
|
@@ -25784,7 +25815,7 @@ const TempNew = {
|
|
|
25784
25815
|
aliases: ['create'],
|
|
25785
25816
|
summary: 'Create a disposable inbox',
|
|
25786
25817
|
usage: ['temp new [--name <local-part>] [--domain <domain>] [--ttl <minutes>]'],
|
|
25787
|
-
description: 'Creates a disposable address that needs no account and no API key, and keeps its token in `~/.openemail/temp-mail.json` so the other `temp` commands can reach it. The address is printed on stdout on its own, so `ADDRESS=$(openemail temp new)` works in a script. It lives for 60 minutes unless you pass `--ttl`, up to 24 hours.',
|
|
25818
|
+
description: 'Creates a disposable address that needs no account and no API key, and keeps its token in `~/.openemail/temp-mail.json` so the other `temp` commands can reach it. The address is printed on stdout on its own, so `ADDRESS=$(openemail temp new)` works in a script. It lives for 60 minutes unless you pass `--ttl`, up to 24 hours, and `openemail temp-mail extend` adds up to an hour at a time within 24 hours of its creation, saving the new token it returns. A name you choose with `--name` is issued to anyone who asks for it too, so leave it out when the mail should reach you alone.',
|
|
25788
25819
|
flags: [TEMP_FLAGS.NAME, TEMP_FLAGS.DOMAIN, TEMP_FLAGS.TTL],
|
|
25789
25820
|
examples: [
|
|
25790
25821
|
{ command: 'openemail temp new' },
|
|
@@ -25821,7 +25852,7 @@ const TempNew = {
|
|
|
25821
25852
|
}
|
|
25822
25853
|
};
|
|
25823
25854
|
|
|
25824
|
-
const PrintDetail = (
|
|
25855
|
+
const PrintDetail = (detail, raw) => {
|
|
25825
25856
|
const view = MessageView(detail.message);
|
|
25826
25857
|
PrintMailMessage({
|
|
25827
25858
|
...view,
|
|
@@ -25831,8 +25862,6 @@ const PrintDetail = (context, detail, raw) => {
|
|
|
25831
25862
|
subject: view.subject || detail.subject,
|
|
25832
25863
|
date: view.date ?? detail.receivedAt
|
|
25833
25864
|
}, raw);
|
|
25834
|
-
if (detail.truncated)
|
|
25835
|
-
context.io.Warn(TEMP_MESSAGES.TRUNCATED);
|
|
25836
25865
|
};
|
|
25837
25866
|
const ListMessages = async (context, target) => {
|
|
25838
25867
|
const baseUrl = TempBaseUrl(context, target);
|
|
@@ -25871,22 +25900,23 @@ const TempRead = {
|
|
|
25871
25900
|
aliases: ['show', 'open'],
|
|
25872
25901
|
summary: 'List the mail in a disposable inbox, or read one message',
|
|
25873
25902
|
usage: ['temp read [inbox] [message-id] [flags]'],
|
|
25874
|
-
description: 'Without a message id it lists what has arrived, newest first, with a short preview that is often enough to read a one time code. With one it prints the whole message, turning HTML into readable text. The inbox is its id or address, and can be left out when you have only one. Reading a message
|
|
25903
|
+
description: 'Without a message id it lists what has arrived, newest first, with a short preview that is often enough to read a one time code. With one it prints the whole message, turning HTML into readable text, and lists its attachments by name and size, without their bytes. The inbox is its id or address, and can be left out when you have only one, so a lone `thr_` id reads that message. Reading a message does not mark it seen, and its body is never cut. The sender of mail in a temporary inbox is never verified.',
|
|
25875
25904
|
arguments: [
|
|
25876
25905
|
{ name: 'inbox', description: 'Inbox id or address from `openemail temp list`' },
|
|
25877
|
-
{ name: 'message-id', description: 'A `
|
|
25906
|
+
{ name: 'message-id', description: 'A `thr_` id from the list' }
|
|
25878
25907
|
],
|
|
25879
25908
|
flags: [TEMP_FLAGS.HTML, TEMP_FLAGS.INBOX_TOKEN],
|
|
25880
25909
|
examples: [
|
|
25881
25910
|
{ command: 'openemail temp read' },
|
|
25882
25911
|
{ command: 'openemail temp read quiet-otter-12@example-temp.com' },
|
|
25883
|
-
{ command: 'openemail temp read
|
|
25884
|
-
{ command: 'openemail temp read
|
|
25912
|
+
{ command: 'openemail temp read thr_9e3b7c1a5f2d8e40b6a9c3f1', note: 'Reads one message from your only inbox' },
|
|
25913
|
+
{ command: 'openemail temp read tinb_k7m2q9xw4bdp thr_9e3b7c1a5f2d8e40b6a9c3f1' },
|
|
25914
|
+
{ command: 'openemail temp read tinb_k7m2q9xw4bdp thr_9e3b7c1a5f2d8e40b6a9c3f1 --html > message.html' }
|
|
25885
25915
|
],
|
|
25886
25916
|
auth: COMMAND_AUTHS.NONE,
|
|
25887
25917
|
run: async (context) => {
|
|
25888
25918
|
const [first, second] = context.args.positional;
|
|
25889
|
-
const firstIsMessage = first !== undefined && second === undefined && first.startsWith(TEMP_MESSAGE_PREFIX);
|
|
25919
|
+
const firstIsMessage = first !== undefined && second === undefined && first.startsWith(TEMP_MESSAGE_PREFIX) && !first.includes('@');
|
|
25890
25920
|
const target = await ResolveTempInbox(context, firstIsMessage ? null : first ?? null);
|
|
25891
25921
|
const messageId = firstIsMessage ? first : second;
|
|
25892
25922
|
if (messageId === undefined)
|
|
@@ -25896,7 +25926,7 @@ const TempRead = {
|
|
|
25896
25926
|
if (context.io.json)
|
|
25897
25927
|
context.io.Json(detail);
|
|
25898
25928
|
else
|
|
25899
|
-
PrintDetail(
|
|
25929
|
+
PrintDetail(detail, Flags$2.Bool(context.args.flags, TEMP_FLAG_NAMES.HTML));
|
|
25900
25930
|
return EXIT_CODES.OK;
|
|
25901
25931
|
}
|
|
25902
25932
|
};
|
|
@@ -25925,11 +25955,18 @@ const Print = (context, messages) => {
|
|
|
25925
25955
|
};
|
|
25926
25956
|
const Left = (expiresAt) => RelativeTime(expiresAt).replace(/^in /, '');
|
|
25927
25957
|
const Waiting = (context, expiresAt) => context.io.Spinner(TEMP_MESSAGES.WAITING(Left(expiresAt)));
|
|
25958
|
+
const Extended = (target, expiresAt) => {
|
|
25959
|
+
if (!target.fromStore)
|
|
25960
|
+
return null;
|
|
25961
|
+
const stored = TempMailStore.Find(target.id);
|
|
25962
|
+
const later = Date.parse(stored?.expiresAt ?? '');
|
|
25963
|
+
return stored !== null && !Number.isNaN(later) && (Number.isNaN(expiresAt) || later > expiresAt) ? stored : null;
|
|
25964
|
+
};
|
|
25928
25965
|
const TempWatch = {
|
|
25929
25966
|
name: TEMP_COMMAND_NAMES.WATCH,
|
|
25930
25967
|
summary: 'Wait for mail to arrive in a disposable inbox',
|
|
25931
25968
|
usage: ['temp watch [inbox] [--first]'],
|
|
25932
|
-
description: 'Prints what is already in the inbox, then checks every 3 seconds and prints each new message as it lands, until you press Ctrl+C or the inbox expires. With `--first` it stops as soon as there is a message, which suits a script waiting for a sign-up code. With `--json` each message is one JSON line on stdout.',
|
|
25969
|
+
description: 'Prints what is already in the inbox, then checks every 3 seconds and prints each new message as it lands, until you press Ctrl+C or the inbox expires. When `openemail temp-mail extend` saves a new token for the inbox meanwhile, the watch carries on with it until the later expiry. With `--first` it stops as soon as there is a message, which suits a script waiting for a sign-up code. With `--json` each message is one JSON line on stdout.',
|
|
25933
25970
|
arguments: [{ name: 'inbox', description: 'Inbox id or address from `openemail temp list`, optional when you have only one' }],
|
|
25934
25971
|
flags: [TEMP_FLAGS.FIRST, TEMP_FLAGS.INBOX_TOKEN],
|
|
25935
25972
|
examples: [
|
|
@@ -25942,7 +25979,8 @@ const TempWatch = {
|
|
|
25942
25979
|
const target = await ResolveTempInbox(context, context.args.positional[0] ?? null);
|
|
25943
25980
|
const first = Flags$2.Bool(context.args.flags, TEMP_FLAG_NAMES.FIRST);
|
|
25944
25981
|
const baseUrl = TempBaseUrl(context, target);
|
|
25945
|
-
const
|
|
25982
|
+
const lease = { token: target.token };
|
|
25983
|
+
const List = () => CallAnonymous(context, TEMP_MESSAGES.LOADING, signal => TempClient(baseUrl, signal).listMessages(target.id, { inboxToken: lease.token, signal }), { spinner: false });
|
|
25946
25984
|
const opening = await List();
|
|
25947
25985
|
const seen = new Set(opening.items.map(message => message.id));
|
|
25948
25986
|
let expiresAt = Date.parse(opening.expiresAt ?? target.expiresAt ?? '');
|
|
@@ -25959,6 +25997,12 @@ const TempWatch = {
|
|
|
25959
25997
|
const spinner = Waiting(context, expiresAt);
|
|
25960
25998
|
try {
|
|
25961
25999
|
for (;;) {
|
|
26000
|
+
const extended = Extended(target, expiresAt);
|
|
26001
|
+
if (extended !== null) {
|
|
26002
|
+
lease.token = extended.inboxToken;
|
|
26003
|
+
expiresAt = Date.parse(extended.expiresAt);
|
|
26004
|
+
spinner.Update(TEMP_MESSAGES.WAITING(Left(expiresAt)));
|
|
26005
|
+
}
|
|
25962
26006
|
if (!Number.isNaN(expiresAt) && Date.now() >= expiresAt) {
|
|
25963
26007
|
spinner.Stop();
|
|
25964
26008
|
context.io.Info(TEMP_MESSAGES.EXPIRED);
|
|
@@ -25966,10 +26010,12 @@ const TempWatch = {
|
|
|
25966
26010
|
}
|
|
25967
26011
|
await Sleep$2(TEMP_LIMITS.WATCH_INTERVAL_MS, context.signal);
|
|
25968
26012
|
const page = await List().catch((error) => {
|
|
25969
|
-
if (error
|
|
26013
|
+
if (InboxGone(error))
|
|
25970
26014
|
return null;
|
|
25971
26015
|
throw error;
|
|
25972
26016
|
});
|
|
26017
|
+
if (page === null && Extended(target, expiresAt) !== null)
|
|
26018
|
+
continue;
|
|
25973
26019
|
if (page === null) {
|
|
25974
26020
|
spinner.Stop();
|
|
25975
26021
|
context.io.Info(TEMP_MESSAGES.EXPIRED);
|
|
@@ -25997,12 +26043,12 @@ const Temp = {
|
|
|
25997
26043
|
name: TEMP_COMMAND_NAMES.TEMP,
|
|
25998
26044
|
summary: 'Disposable inboxes for sign-ups and tests',
|
|
25999
26045
|
usage: ['temp <new|list|read|watch|delete> [arguments] [flags]'],
|
|
26000
|
-
description: 'Creates short-lived addresses that anyone can use without signing in, and reads the mail they receive.
|
|
26046
|
+
description: 'Creates short-lived addresses that anyone can use without signing in, and reads the mail they receive. The inbox token is the lease itself and nothing about it is stored on the server, so it is kept in `~/.openemail/temp-mail.json` (mode 0600) and later commands find the inbox by its id or address. A lost token cannot be recovered. Mail that lands in a disposable inbox comes from strangers and its sender is never verified.',
|
|
26001
26047
|
subcommands: [TempNew, TempList, TempRead, TempWatch, TempDelete],
|
|
26002
26048
|
examples: [
|
|
26003
26049
|
{ command: 'openemail temp new', note: 'Prints a fresh address' },
|
|
26004
26050
|
{ command: 'openemail temp watch', note: 'Waits for mail and prints each message as it lands' },
|
|
26005
|
-
{ command: 'openemail temp read
|
|
26051
|
+
{ command: 'openemail temp read thr_9e3b7c1a5f2d8e40b6a9c3f1', note: 'Reads one message by its thread id' },
|
|
26006
26052
|
{ command: 'openemail temp delete --yes' }
|
|
26007
26053
|
],
|
|
26008
26054
|
section: HELP_SECTIONS.MAIL,
|
|
@@ -26329,6 +26375,13 @@ const RememberRotatedKey = (context, verb, call, value) => {
|
|
|
26329
26375
|
};
|
|
26330
26376
|
|
|
26331
26377
|
const Text = (value) => typeof value === 'string' && value.length > 0 ? value : null;
|
|
26378
|
+
const Renew = (id, address, token, expiresAt, baseUrl) => {
|
|
26379
|
+
const stored = TempMailStore.Find(id);
|
|
26380
|
+
if (stored === null || OriginOf(stored.baseUrl ?? DEFAULT_API_URL) !== OriginOf(baseUrl))
|
|
26381
|
+
return;
|
|
26382
|
+
if (TempMailStore.Renew(id, token, expiresAt))
|
|
26383
|
+
Console.Info(RESOURCE_MESSAGES.INBOX_RENEWED(Color.Sanitize(address)));
|
|
26384
|
+
};
|
|
26332
26385
|
const RememberTempInbox = (verb, call, value, baseUrl) => {
|
|
26333
26386
|
if (verb.namespace !== RESOURCE_TEMP_INBOX.NAMESPACE)
|
|
26334
26387
|
return;
|
|
@@ -26338,7 +26391,9 @@ const RememberTempInbox = (verb, call, value, baseUrl) => {
|
|
|
26338
26391
|
TempMailStore.Remove(inbox);
|
|
26339
26392
|
return;
|
|
26340
26393
|
}
|
|
26341
|
-
if (call.method !== RESOURCE_TEMP_INBOX.CREATE
|
|
26394
|
+
if (call.method !== RESOURCE_TEMP_INBOX.CREATE && call.method !== RESOURCE_TEMP_INBOX.EXTEND)
|
|
26395
|
+
return;
|
|
26396
|
+
if (!IsPlainObject(value))
|
|
26342
26397
|
return;
|
|
26343
26398
|
const id = Text(value.id);
|
|
26344
26399
|
const address = Text(value.address);
|
|
@@ -26346,6 +26401,10 @@ const RememberTempInbox = (verb, call, value, baseUrl) => {
|
|
|
26346
26401
|
const expiresAt = Text(value.expiresAt);
|
|
26347
26402
|
if (id === null || address === null || token === null || expiresAt === null)
|
|
26348
26403
|
return;
|
|
26404
|
+
if (call.method === RESOURCE_TEMP_INBOX.EXTEND) {
|
|
26405
|
+
Renew(id, address, token, expiresAt, baseUrl);
|
|
26406
|
+
return;
|
|
26407
|
+
}
|
|
26349
26408
|
TempMailStore.Add({ id, address, inboxToken: token, expiresAt, baseUrl });
|
|
26350
26409
|
Console.Info(RESOURCE_MESSAGES.INBOX_SAVED(Color.Sanitize(address)));
|
|
26351
26410
|
};
|
|
@@ -26578,7 +26637,7 @@ const RunVerb = (namespace, verb) => async (context) => {
|
|
|
26578
26637
|
|
|
26579
26638
|
const Code = (text) => `${CODE_SPAN_DELIMITER}${text}${CODE_SPAN_DELIMITER}`;
|
|
26580
26639
|
const VerbProse = (verb, text) => {
|
|
26581
|
-
let prose = text;
|
|
26640
|
+
let prose = text.replace(RESOURCE_EMPTY_STRING_CLEAR, '$1');
|
|
26582
26641
|
for (const flag of verb.flags) {
|
|
26583
26642
|
if (flag.parameter === null)
|
|
26584
26643
|
continue;
|
|
@@ -26609,7 +26668,10 @@ const VerbArguments = (verb) => verb.positionals.map(positional => ({
|
|
|
26609
26668
|
|
|
26610
26669
|
const VerbDescription = (verb) => {
|
|
26611
26670
|
const prose = VerbProse(verb, verb.entry.description).trim();
|
|
26612
|
-
|
|
26671
|
+
if (!verb.paginates)
|
|
26672
|
+
return prose;
|
|
26673
|
+
const paging = verb.returns.layout === MANIFEST_DEFAULT_PAGE_LAYOUT ? RESOURCE_HELP.PAGING : RESOURCE_HELP.PAGING_OWN_SHAPE;
|
|
26674
|
+
return `${prose}\n\n${paging}`;
|
|
26613
26675
|
};
|
|
26614
26676
|
|
|
26615
26677
|
const Collect = (fields, example, pattern, value) => {
|
|
@@ -26706,9 +26768,10 @@ const VerbExamples = (namespace, verb, known) => {
|
|
|
26706
26768
|
})
|
|
26707
26769
|
.slice(0, RESOURCE_EXAMPLE_OPTIONAL_FLAGS)
|
|
26708
26770
|
.flat();
|
|
26709
|
-
const
|
|
26771
|
+
const emptyPatch = verb.entry.method.toUpperCase() === RESOURCE_PATCH_METHOD && required.length === 0 && optional.length > 0;
|
|
26772
|
+
const examples = emptyPatch ? [] : [{ command: Join(essential) }];
|
|
26710
26773
|
if (optional.length > 0) {
|
|
26711
|
-
if (required.length > 0 || verb.positionals.some(positional => positional.required))
|
|
26774
|
+
if (!emptyPatch && (required.length > 0 || verb.positionals.some(positional => positional.required)))
|
|
26712
26775
|
examples[0].note = RESOURCE_EXAMPLE_NOTES.REQUIRED;
|
|
26713
26776
|
examples.push({ command: Join([...essential, ...optional]), note: RESOURCE_EXAMPLE_NOTES.FULL });
|
|
26714
26777
|
}
|
|
@@ -26729,7 +26792,7 @@ const VerbExamples = (namespace, verb, known) => {
|
|
|
26729
26792
|
}
|
|
26730
26793
|
const reads = verb.entry.method.toUpperCase() === RESOURCE_READ_METHOD && verb.returns.shape === MANIFEST_RETURN_SHAPES.OBJECT;
|
|
26731
26794
|
if (examples.length < 2 || reads) {
|
|
26732
|
-
examples.push({ command: Join([...essential, `--${GLOBAL_FLAG_NAMES.JSON}`]), note: RESOURCE_EXAMPLE_NOTES.JSON });
|
|
26795
|
+
examples.push({ command: Join([...essential, ...(emptyPatch ? optional : []), `--${GLOBAL_FLAG_NAMES.JSON}`]), note: RESOURCE_EXAMPLE_NOTES.JSON });
|
|
26733
26796
|
}
|
|
26734
26797
|
return examples.slice(0, RESOURCE_EXAMPLE_LIMIT);
|
|
26735
26798
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openemail/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"author": "OpenEmail",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"description": "The official OpenEmail command line. Sign in once, then send and read mail, manage domains, addresses, keys, webhooks, templates, contacts, audiences and every other OpenEmail resource, use the AI tools and connect MCP clients from your terminal.",
|