@openemail/cli 0.0.1 → 0.0.2

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.
Files changed (2) hide show
  1. package/openemail.js +36 -26
  2. package/package.json +1 -1
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) => `OpenEmail CLI on ${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.1";
1115
+ var version$1 = "0.0.2";
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.5";
1248
+ var version = "0.0.6";
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 ! # $ % & \' * + / = ? ^ _ ` { | } ~ - are allowed, and so are dots between them. `*` on its own is how the catch-all is written and is refused.' },
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.' }
@@ -13921,6 +13922,7 @@ const RESOURCE_ID_LIST_KEY = 'ids';
13921
13922
  const RESOURCE_ID_LIST_SUFFIX = 'Ids';
13922
13923
  const RESOURCE_MAX_SAMPLE = '100';
13923
13924
  const RESOURCE_READ_METHOD = 'GET';
13925
+ const RESOURCE_PATCH_METHOD = 'PATCH';
13924
13926
  const RESOURCE_EXAMPLE_LIMIT = 5;
13925
13927
  const RESOURCE_EXAMPLE_OPTIONAL_FLAGS = 3;
13926
13928
  const RESOURCE_CONFIRM_TARGET_LIMIT = 5;
@@ -13941,6 +13943,7 @@ const RESOURCE_UNSUPPORTED_ESCAPE = /\\[^'\\]/;
13941
13943
  const RESOURCE_SECRET_FLAG = /(key|token|secret|password)$/;
13942
13944
  const RESOURCE_SENTENCE_BREAK = /(?<=[.!?])\s+(?=[A-Z`])/;
13943
13945
  const RESOURCE_CAMEL_PATH = /^[a-z]+[A-Z][A-Za-z]*$/;
13946
+ const RESOURCE_EMPTY_STRING_CLEAR = /(\b[Nn]ull\b|`null`) or an empty string/g;
13944
13947
  const RESOURCE_EXAMPLE_PATTERNS = {
13945
13948
  STRING: /(\w+):\s*'((?:[^'\\\n]|\\.)*)'/g,
13946
13949
  NUMBER: /(\w+):\s*(-?\d+(?:\.\d+)?)(?=\s*[,}\n])/g,
@@ -13979,6 +13982,7 @@ const RESOURCE_HELP = {
13979
13982
  REQUIRED: 'Required.',
13980
13983
  REQUIRED_WITH_DATA: 'Required, here or in `--data`.',
13981
13984
  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.',
13985
+ 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
13986
  DATA_USAGE: '--data <json|@file|->',
13983
13987
  JSON_TYPE: (type) => `JSON shaped as \`${type}\`, inline or from a file with @path.`
13984
13988
  };
@@ -14268,6 +14272,7 @@ const TERMINAL = {
14268
14272
  MIN_WRAP_WIDTH: 16
14269
14273
  };
14270
14274
  const CODE_SPAN_DELIMITER = '`';
14275
+ const CODE_SPAN_PATTERN = /`([^`\s](?:[^`]*[^`\s])?)`/g;
14271
14276
  const BANNER_LINES = [
14272
14277
  ' _ _',
14273
14278
  ' ___ _ __ ___ _ _ ___ _ __ __ _ (_)| |',
@@ -15535,13 +15540,7 @@ const Config$1 = {
15535
15540
  Update: Update$1
15536
15541
  };
15537
15542
 
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
- };
15543
+ const FormatCode = (text, paint) => text.replace(CODE_SPAN_PATTERN, (_span, code) => paint(code));
15545
15544
 
15546
15545
  const KNOWN_KINDS = new Set(Object.values(PROFILE_KINDS));
15547
15546
  const IsUsable = (value) => IsRecord$2(value) && KNOWN_KINDS.has(value.kind) && typeof value.baseUrl === 'string';
@@ -17622,6 +17621,17 @@ const NonEmptyEnv = (name) => {
17622
17621
  return value === undefined || value.length === 0 ? null : value;
17623
17622
  };
17624
17623
 
17624
+ 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;
17625
+
17626
+ const withScheme = (value) => {
17627
+ const trimmed = value.trim();
17628
+ if (trimmed === '' || /^https?:\/\//i.test(trimmed))
17629
+ return trimmed;
17630
+ const bare = trimmed.replace(/^\/+/, '');
17631
+ return `${LOCAL_NETWORK.test(bare) ? 'http' : 'https'}://${bare}`;
17632
+ };
17633
+ const publicUrl = (value) => withScheme(value).replace(/\/+$/, '');
17634
+
17625
17635
  const Invalid = (value) => new UsageError(MESSAGES.INVALID_BASE_URL(Color.Sanitize(value)), { code: CLI_ERROR_CODES.INVALID_VALUE });
17626
17636
  const Parse$3 = (url) => {
17627
17637
  try {
@@ -17641,8 +17651,8 @@ const Unspecified = (normalised, url) => new UsageError(MESSAGES.UNSPECIFIED_HOS
17641
17651
  next: MESSAGES.UNSPECIFIED_HOST_NEXT(Color.Sanitize(LoopbackInstead(url)))
17642
17652
  });
17643
17653
  const NormaliseUrl = (value) => {
17644
- const normalised = NormaliseEndpoint(value);
17645
- if (normalised === undefined)
17654
+ const normalised = publicUrl(value);
17655
+ if (normalised.length === 0)
17646
17656
  throw Invalid(value);
17647
17657
  const url = Parse$3(normalised);
17648
17658
  if (url === null || (url.protocol !== URL_PROTOCOLS.HTTP && url.protocol !== URL_PROTOCOLS.HTTPS))
@@ -19257,11 +19267,7 @@ const PathOf = (raw) => {
19257
19267
  return raw;
19258
19268
  }
19259
19269
  };
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
- };
19270
+ const WithoutDocsPrefix = (path) => path.replace(DOCS_PATTERNS.DOCS_PREFIX, '');
19265
19271
  const Decoded = (segment) => {
19266
19272
  try {
19267
19273
  return decodeURIComponent(segment);
@@ -26578,7 +26584,7 @@ const RunVerb = (namespace, verb) => async (context) => {
26578
26584
 
26579
26585
  const Code = (text) => `${CODE_SPAN_DELIMITER}${text}${CODE_SPAN_DELIMITER}`;
26580
26586
  const VerbProse = (verb, text) => {
26581
- let prose = text;
26587
+ let prose = text.replace(RESOURCE_EMPTY_STRING_CLEAR, '$1');
26582
26588
  for (const flag of verb.flags) {
26583
26589
  if (flag.parameter === null)
26584
26590
  continue;
@@ -26609,7 +26615,10 @@ const VerbArguments = (verb) => verb.positionals.map(positional => ({
26609
26615
 
26610
26616
  const VerbDescription = (verb) => {
26611
26617
  const prose = VerbProse(verb, verb.entry.description).trim();
26612
- return verb.paginates ? `${prose}\n\n${RESOURCE_HELP.PAGING}` : prose;
26618
+ if (!verb.paginates)
26619
+ return prose;
26620
+ const paging = verb.returns.layout === MANIFEST_DEFAULT_PAGE_LAYOUT ? RESOURCE_HELP.PAGING : RESOURCE_HELP.PAGING_OWN_SHAPE;
26621
+ return `${prose}\n\n${paging}`;
26613
26622
  };
26614
26623
 
26615
26624
  const Collect = (fields, example, pattern, value) => {
@@ -26706,9 +26715,10 @@ const VerbExamples = (namespace, verb, known) => {
26706
26715
  })
26707
26716
  .slice(0, RESOURCE_EXAMPLE_OPTIONAL_FLAGS)
26708
26717
  .flat();
26709
- const examples = [{ command: Join(essential) }];
26718
+ const emptyPatch = verb.entry.method.toUpperCase() === RESOURCE_PATCH_METHOD && required.length === 0 && optional.length > 0;
26719
+ const examples = emptyPatch ? [] : [{ command: Join(essential) }];
26710
26720
  if (optional.length > 0) {
26711
- if (required.length > 0 || verb.positionals.some(positional => positional.required))
26721
+ if (!emptyPatch && (required.length > 0 || verb.positionals.some(positional => positional.required)))
26712
26722
  examples[0].note = RESOURCE_EXAMPLE_NOTES.REQUIRED;
26713
26723
  examples.push({ command: Join([...essential, ...optional]), note: RESOURCE_EXAMPLE_NOTES.FULL });
26714
26724
  }
@@ -26729,7 +26739,7 @@ const VerbExamples = (namespace, verb, known) => {
26729
26739
  }
26730
26740
  const reads = verb.entry.method.toUpperCase() === RESOURCE_READ_METHOD && verb.returns.shape === MANIFEST_RETURN_SHAPES.OBJECT;
26731
26741
  if (examples.length < 2 || reads) {
26732
- examples.push({ command: Join([...essential, `--${GLOBAL_FLAG_NAMES.JSON}`]), note: RESOURCE_EXAMPLE_NOTES.JSON });
26742
+ examples.push({ command: Join([...essential, ...(emptyPatch ? optional : []), `--${GLOBAL_FLAG_NAMES.JSON}`]), note: RESOURCE_EXAMPLE_NOTES.JSON });
26733
26743
  }
26734
26744
  return examples.slice(0, RESOURCE_EXAMPLE_LIMIT);
26735
26745
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openemail/cli",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
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.",