@herbertgao/pi-extensions 2026.8.7 → 2026.8.8

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 (99) hide show
  1. package/README.md +7 -5
  2. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +1 -1
  3. package/node_modules/@herbertgao/pi-cc-extensions/README.md +1 -1
  4. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +63 -15
  5. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/message-display.ts +78 -16
  6. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +13 -0
  7. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +17 -6
  8. package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
  9. package/node_modules/@juicesharp/rpiv-ask-user-question/README.md +4 -4
  10. package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +154 -96
  11. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/hosts.md +3 -0
  12. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/keyboard.md +10 -2
  13. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/localization.md +7 -5
  14. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/tool-schema.md +11 -5
  15. package/node_modules/@juicesharp/rpiv-ask-user-question/locales/en.json +3 -0
  16. package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +2 -2
  17. package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +137 -96
  18. package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +44 -32
  19. package/node_modules/@juicesharp/rpiv-ask-user-question/state/selectors/projections.ts +22 -15
  20. package/node_modules/@juicesharp/rpiv-ask-user-question/state/state-reducer.ts +11 -1
  21. package/node_modules/@juicesharp/rpiv-ask-user-question/tool/response-envelope.ts +11 -0
  22. package/node_modules/@juicesharp/rpiv-ask-user-question/tool/types.ts +11 -0
  23. package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/multi-select-view.ts +31 -17
  24. package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/preview/preview-block-renderer.ts +15 -6
  25. package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/wrapping-select.ts +15 -6
  26. package/node_modules/@juicesharp/rpiv-ask-user-question/view/dialog-builder.ts +56 -34
  27. package/node_modules/@juicesharp/rpiv-ask-user-question/view/tab-content-strategy.ts +50 -4
  28. package/node_modules/@narumitw/pi-btw/package.json +2 -2
  29. package/node_modules/@pi-plugins/fast-mode/dist/index.mjs +44 -1856
  30. package/node_modules/@pi-plugins/fast-mode/dist/index.mjs.map +1 -1
  31. package/node_modules/@pi-plugins/fast-mode/package.json +4 -6
  32. package/node_modules/pi-lens/CHANGELOG.md +90 -0
  33. package/node_modules/pi-lens/README.md +106 -0
  34. package/node_modules/pi-lens/dist/clients/bash-file-access.js +97 -6
  35. package/node_modules/pi-lens/dist/clients/blocker-past-eof.js +16 -6
  36. package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +14 -0
  37. package/node_modules/pi-lens/dist/clients/cache-observability.js +353 -76
  38. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +22 -1
  39. package/node_modules/pi-lens/dist/clients/demoted-finding-render.js +155 -0
  40. package/node_modules/pi-lens/dist/clients/deps/pi-tui.js +8 -2
  41. package/node_modules/pi-lens/dist/clients/deps/typebox.js +8 -3
  42. package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +9 -6
  43. package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +9 -7
  44. package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +8 -6
  45. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +8 -10
  46. package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +10 -10
  47. package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +20 -5
  48. package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +44 -11
  49. package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +10 -15
  50. package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +19 -11
  51. package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +35 -13
  52. package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +8 -8
  53. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +110 -23
  54. package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +12 -7
  55. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +117 -1
  56. package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +16 -14
  57. package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +8 -9
  58. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +22 -0
  59. package/node_modules/pi-lens/dist/clients/jscpd-client.js +5 -2
  60. package/node_modules/pi-lens/dist/clients/language-profile.js +7 -2
  61. package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +27 -2
  62. package/node_modules/pi-lens/dist/clients/lsp/client.js +144 -5
  63. package/node_modules/pi-lens/dist/clients/lsp/index.js +151 -6
  64. package/node_modules/pi-lens/dist/clients/module-report.js +14 -1
  65. package/node_modules/pi-lens/dist/clients/ndjson-logger.js +114 -23
  66. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +18 -9
  67. package/node_modules/pi-lens/dist/clients/project-report.js +62 -27
  68. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +7 -2
  69. package/node_modules/pi-lens/dist/clients/read-guard.js +88 -19
  70. package/node_modules/pi-lens/dist/clients/review-graph/builder.js +194 -31
  71. package/node_modules/pi-lens/dist/clients/review-graph/revision-drift.js +21 -0
  72. package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +6 -3
  73. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +44 -0
  74. package/node_modules/pi-lens/dist/clients/runtime-session.js +41 -0
  75. package/node_modules/pi-lens/dist/clients/runtime-turn.js +47 -2
  76. package/node_modules/pi-lens/dist/clients/session-event-guard.js +184 -0
  77. package/node_modules/pi-lens/dist/clients/source-filter.js +6 -2
  78. package/node_modules/pi-lens/dist/clients/startup-scan.js +5 -2
  79. package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +1 -0
  80. package/node_modules/pi-lens/dist/index.js +1224 -393
  81. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +10 -3
  82. package/node_modules/pi-lens/package.json +18 -5
  83. package/node_modules/pi-lens/scripts/install-selftest.mjs +99 -11
  84. package/node_modules/pi-lens/scripts/lib/host-provided-deps.mjs +75 -0
  85. package/node_modules/pi-lens/scripts/lib/warm-loader-cache.mjs +285 -0
  86. package/node_modules/pi-lens/scripts/warm-loader-cache.mjs +233 -0
  87. package/node_modules/pi-web-access/CHANGELOG.md +14 -0
  88. package/node_modules/pi-web-access/README.md +6 -5
  89. package/node_modules/pi-web-access/chrome-cookies.ts +86 -15
  90. package/node_modules/pi-web-access/data-uri-sanitize.ts +406 -0
  91. package/node_modules/pi-web-access/extract.ts +12 -2
  92. package/node_modules/pi-web-access/firecrawl.ts +18 -2
  93. package/node_modules/pi-web-access/github-extract.ts +65 -36
  94. package/node_modules/pi-web-access/index.ts +19 -4
  95. package/node_modules/pi-web-access/openai-search.ts +17 -6
  96. package/node_modules/pi-web-access/package.json +1 -1
  97. package/node_modules/pi-web-access/pdf-extract.ts +18 -5
  98. package/node_modules/pi-web-access/ssrf-protection.ts +12 -2
  99. package/package.json +9 -9
@@ -34,7 +34,7 @@ import {
34
34
  type SummaryMeta,
35
35
  } from "./summary-review.ts";
36
36
  import { randomUUID } from "node:crypto";
37
- import { execFileSync } from "node:child_process";
37
+ import { execFileSync, spawn } from "node:child_process";
38
38
  import { createRequire } from "node:module";
39
39
  import { platform } from "node:os";
40
40
  import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
@@ -744,11 +744,26 @@ function closeCurator(callId?: string): void {
744
744
 
745
745
  async function openInBrowser(pi: ExtensionAPI, url: string): Promise<void> {
746
746
  const plat = platform();
747
+ if (plat !== "darwin" && plat !== "win32") {
748
+ await new Promise<void>((resolve, reject) => {
749
+ const child = spawn("xdg-open", [url], { detached: true, stdio: "ignore" });
750
+ const timer = setTimeout(resolve, 100);
751
+ child.once("error", (err) => {
752
+ clearTimeout(timer);
753
+ reject(err);
754
+ });
755
+ child.once("exit", (code) => {
756
+ clearTimeout(timer);
757
+ if (code === 0) resolve();
758
+ else reject(new Error(`Failed to open browser (exit code ${code ?? "unknown"})`));
759
+ });
760
+ child.unref();
761
+ });
762
+ return;
763
+ }
747
764
  const result = plat === "darwin"
748
765
  ? await pi.exec("open", [url])
749
- : plat === "win32"
750
- ? await pi.exec("cmd", ["/c", "start", "", url])
751
- : await pi.exec("xdg-open", [url]);
766
+ : await pi.exec("cmd", ["/c", "start", "", url]);
752
767
  if (result.code !== 0) {
753
768
  throw new Error(result.stderr || `Failed to open browser (exit code ${result.code})`);
754
769
  }
@@ -19,7 +19,7 @@ const MODEL_PREFERENCE = [
19
19
  (id: string) => id.includes("terra"),
20
20
  (id: string) => /^gpt-\d+(\.\d+)?$/.test(id),
21
21
  ];
22
- const SEARCH_PROVIDERS = ["openai-codex", "openai"] as const;
22
+ const DEFAULT_SEARCH_PROVIDERS: readonly string[] = ["openai-codex", "openai"];
23
23
 
24
24
  function pickSearchModel<T extends { id: string }>(models: readonly T[]): T | undefined {
25
25
  const candidates = models
@@ -36,12 +36,13 @@ interface WebSearchConfig {
36
36
  openaiApiKey?: unknown;
37
37
  openaiResponsesUrl?: unknown;
38
38
  openaiSearchModel?: unknown;
39
+ openaiSearchProviders?: unknown;
39
40
  }
40
41
 
41
42
  type ProviderHeaders = Record<string, string | null>;
42
43
 
43
44
  interface OpenAIAuth {
44
- provider: "openai-codex" | "openai";
45
+ provider: string;
45
46
  apiKey: string;
46
47
  model: string;
47
48
  headers: ProviderHeaders;
@@ -149,6 +150,14 @@ function resolveConfiguredResponsesUrl(value: unknown): string {
149
150
  return url.toString();
150
151
  }
151
152
 
153
+ function resolveConfiguredSearchProviders(value: unknown): readonly string[] {
154
+ if (value === undefined) return DEFAULT_SEARCH_PROVIDERS;
155
+ if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string" || entry.trim().length === 0)) {
156
+ throw new Error(`openaiSearchProviders in ${CONFIG_PATH} must be an array of non-empty Pi provider ids`);
157
+ }
158
+ return value.map((entry) => entry.trim());
159
+ }
160
+
152
161
  function resolveConfiguredSearchModel(value: unknown): string | undefined {
153
162
  if (value == null) return undefined;
154
163
  if (typeof value !== "string" || value.trim().length === 0) {
@@ -165,14 +174,14 @@ function toRequestHeaders(headers: ProviderHeaders): Record<string, string> {
165
174
  return requestHeaders;
166
175
  }
167
176
 
168
- async function resolvePiAuth(ctx: ExtensionContext, responsesUrl: string, modelOverride?: string): Promise<OpenAIAuth | undefined> {
177
+ async function resolvePiAuth(ctx: ExtensionContext, responsesUrl: string, providers: readonly string[], modelOverride?: string): Promise<OpenAIAuth | undefined> {
169
178
  let models: ReturnType<typeof ctx.modelRegistry.getAll>;
170
179
  try {
171
180
  models = ctx.modelRegistry.getAll();
172
181
  } catch {
173
182
  return undefined;
174
183
  }
175
- for (const provider of SEARCH_PROVIDERS) {
184
+ for (const provider of providers) {
176
185
  const preferred = pickSearchModel(models.filter((model) => model.provider === provider));
177
186
  if (!preferred) continue;
178
187
  try {
@@ -196,8 +205,9 @@ export async function resolveOpenAIAuth(ctx?: ExtensionContext, signal?: AbortSi
196
205
  const config = loadConfig();
197
206
  const responsesUrl = resolveConfiguredResponsesUrl(config.openaiResponsesUrl);
198
207
  const modelOverride = resolveConfiguredSearchModel(config.openaiSearchModel);
208
+ const providers = resolveConfiguredSearchProviders(config.openaiSearchProviders);
199
209
  if (ctx) {
200
- const auth = await resolvePiAuth(ctx, responsesUrl, modelOverride);
210
+ const auth = await resolvePiAuth(ctx, responsesUrl, providers, modelOverride);
201
211
  if (auth) return auth;
202
212
  }
203
213
 
@@ -221,7 +231,8 @@ export async function resolveOpenAIAuth(ctx?: ExtensionContext, signal?: AbortSi
221
231
  export async function isOpenAISearchAvailable(ctx?: ExtensionContext): Promise<boolean> {
222
232
  const config = loadConfig();
223
233
  const responsesUrl = resolveConfiguredResponsesUrl(config.openaiResponsesUrl);
224
- if (ctx && await resolvePiAuth(ctx, responsesUrl)) return true;
234
+ const providers = resolveConfiguredSearchProviders(config.openaiSearchProviders);
235
+ if (ctx && await resolvePiAuth(ctx, responsesUrl, providers)) return true;
225
236
  return hasCredentialSource({
226
237
  provider: "OpenAI",
227
238
  configuredValue: config.openaiApiKey,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-web-access",
3
- "version": "0.24.0",
3
+ "version": "0.24.1",
4
4
  "description": "Web search, URL fetching, GitHub repo cloning, PDF extraction, YouTube video understanding, and local video analysis for Pi coding agent. Supports OpenAI, Brave, Parallel, TinyFish, Search1API, Searchinfinity, Querit, Tavily, Firecrawl, Jina, SERPdive, Kagi, Ollama, AnySearch, Bright Data SERP, SerpBase, SearXNG, Exa, Perplexity, and Gemini.",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -51,6 +51,7 @@ export const PDF_PROVIDER_VALUES = new Set<PDFProvider>([
51
51
  export interface PDFConfig {
52
52
  enabled: boolean;
53
53
  maxSizeMB: number;
54
+ maxPages: number;
54
55
  provider: PDFProvider;
55
56
  datalabMode: DatalabMode;
56
57
  datalabTimeoutMs: number;
@@ -69,6 +70,7 @@ export function loadPDFConfig(): PDFConfig {
69
70
  return {
70
71
  enabled: true,
71
72
  maxSizeMB: DEFAULT_PDF_MAX_SIZE_MB,
73
+ maxPages: DEFAULT_MAX_PAGES,
72
74
  provider: "auto",
73
75
  datalabMode: normalizeDatalabMode(process.env.DATALAB_MODE),
74
76
  datalabTimeoutMs: DEFAULT_DATALAB_TIMEOUT_MS,
@@ -98,6 +100,13 @@ export function loadPDFConfig(): PDFConfig {
98
100
  configured > 0
99
101
  ? Math.min(configured, MAX_PDF_MAX_SIZE_MB)
100
102
  : DEFAULT_PDF_MAX_SIZE_MB;
103
+ const configuredMaxPages = pdf.maxPages;
104
+ const maxPages =
105
+ typeof configuredMaxPages === "number" &&
106
+ Number.isFinite(configuredMaxPages) &&
107
+ configuredMaxPages > 0
108
+ ? Math.max(1, Math.floor(configuredMaxPages))
109
+ : DEFAULT_MAX_PAGES;
101
110
 
102
111
  const provider =
103
112
  typeof pdf.provider === "string" &&
@@ -120,6 +129,7 @@ export function loadPDFConfig(): PDFConfig {
120
129
  return {
121
130
  enabled,
122
131
  maxSizeMB: normalized,
132
+ maxPages,
123
133
  provider,
124
134
  datalabMode,
125
135
  datalabTimeoutMs,
@@ -155,18 +165,21 @@ export async function extractPDFToMarkdown(
155
165
  options: PDFExtractOptions = {},
156
166
  ): Promise<PDFExtractResult> {
157
167
  const {
158
- maxPages = DEFAULT_MAX_PAGES,
168
+ maxPages,
159
169
  outputDir = DEFAULT_OUTPUT_DIR,
160
170
  filename,
161
171
  signal,
162
172
  geminiTimeoutMs,
163
173
  } = options;
164
174
 
165
- const safeMaxPages = Number.isFinite(maxPages)
166
- ? Math.max(1, Math.floor(maxPages))
167
- : DEFAULT_MAX_PAGES;
168
- const urlTitle = extractTitleFromURL(url);
169
175
  const pdfConfig = loadPDFConfig();
176
+ const safeMaxPages =
177
+ maxPages === undefined
178
+ ? pdfConfig.maxPages
179
+ : Number.isFinite(maxPages)
180
+ ? Math.max(1, Math.floor(maxPages))
181
+ : DEFAULT_MAX_PAGES;
182
+ const urlTitle = extractTitleFromURL(url);
170
183
  const provider = pdfConfig.provider;
171
184
 
172
185
  if (provider === "auto" || provider === "datalab") {
@@ -5,6 +5,7 @@ import { getWebSearchConfigPath } from "./utils.ts";
5
5
 
6
6
  const DEFAULT_MAX_REDIRECTS = 5;
7
7
  const REDIRECT_STATUSES = new Set([301, 302, 303, 307, 308]);
8
+ const LOOPBACK_ALLOW_RANGES = ["127.0.0.0/8", "::1", "::ffff:127.0.0.0/104"];
8
9
 
9
10
  export type LookupAddress = { address: string; family: number };
10
11
  export type Lookup = (hostname: string) => Promise<LookupAddress[]>;
@@ -152,6 +153,8 @@ interface ValidationOptions {
152
153
  * the local SSRF preflight. This does not configure proxy transport.
153
154
  */
154
155
  trustEnvProxy?: boolean;
156
+ /** Allow loopback URLs for explicit provider base endpoints, not fetched targets. */
157
+ allowLoopback?: boolean;
155
158
  }
156
159
 
157
160
  /** Parsed entry from `allowRanges`: a network address (4 or 16 bytes) + prefix length. */
@@ -185,7 +188,11 @@ export async function validateRemoteUrl(rawUrl: string | URL, options: Validatio
185
188
 
186
189
  const hostname = normalizeHostname(url.hostname);
187
190
  if (!hostname) throw new Error("URL must include a hostname");
188
- if (hostname === "localhost" || hostname.endsWith(".localhost")) {
191
+ if (hostname === "localhost") {
192
+ if (options.allowLoopback === true) return url;
193
+ throw new Error(`Blocked internal hostname: ${hostname}`);
194
+ }
195
+ if (hostname.endsWith(".localhost")) {
189
196
  throw new Error(`Blocked internal hostname: ${hostname}`);
190
197
  }
191
198
 
@@ -193,7 +200,10 @@ export async function validateRemoteUrl(rawUrl: string | URL, options: Validatio
193
200
  assertDomainPolicy(hostname, options.domainPolicy);
194
201
 
195
202
  if (net.isIP(hostname)) {
196
- assertPublicAddress(hostname, hostname, allowRanges);
203
+ const addressAllowRanges = options.allowLoopback === true
204
+ ? [...allowRanges, ...parseAllowRanges(LOOPBACK_ALLOW_RANGES)]
205
+ : allowRanges;
206
+ assertPublicAddress(hostname, hostname, addressAllowRanges);
197
207
  return url;
198
208
  }
199
209
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@herbertgao/pi-extensions",
3
- "version": "2026.8.7",
3
+ "version": "2026.8.8",
4
4
  "description": "Aggregate installer for HerbertGao-maintained Pi extensions.",
5
5
  "keywords": [
6
6
  "extensions",
@@ -42,7 +42,7 @@
42
42
  "@earendil-works/pi-tui": "^0.84.2",
43
43
  "@effect/platform-node": "4.0.0-beta.103",
44
44
  "@effect/platform-node-shared": "4.0.0-beta.103",
45
- "@herbertgao/pi-cc-extensions": "0.8.56",
45
+ "@herbertgao/pi-cc-extensions": "0.8.57",
46
46
  "@herbertgao/pi-copy-response": "0.2.6",
47
47
  "@herbertgao/pi-handoff": "1.1.2",
48
48
  "@herbertgao/pi-inline-skills": "1.0.5",
@@ -53,18 +53,18 @@
53
53
  "@herbertgao/pi-stash": "0.1.1",
54
54
  "@herbertgao/pi-subagents": "0.16.0",
55
55
  "@herbertgao/pi-titlebar-spinner": "0.1.3",
56
- "@juicesharp/rpiv-ask-user-question": "2.6.4",
57
- "@juicesharp/rpiv-config": "^2.6.4",
56
+ "@juicesharp/rpiv-ask-user-question": "2.7.0",
57
+ "@juicesharp/rpiv-config": "^2.7.0",
58
58
  "@modelcontextprotocol/client": "2.0.0",
59
59
  "@modelcontextprotocol/core": "2.0.0",
60
60
  "@modelcontextprotocol/ext-apps": "^1.2.2",
61
61
  "@modelcontextprotocol/sdk": "^1.29.0",
62
62
  "@mozilla/readability": "^0.6.0",
63
63
  "@napi-rs/keyring": "^1.3.0",
64
- "@narumitw/pi-btw": "0.55.0",
65
- "@narumitw/pi-tui-kit": "^0.56.0",
64
+ "@narumitw/pi-btw": "0.55.1",
65
+ "@narumitw/pi-tui-kit": "^0.57.0",
66
66
  "@noble/ed25519": "^3.1.0",
67
- "@pi-plugins/fast-mode": "0.1.9",
67
+ "@pi-plugins/fast-mode": "0.1.10",
68
68
  "@shikijs/cli": "^4.0.2",
69
69
  "@sinclair/typebox": "^0.34.49",
70
70
  "ajv": "^8.17.1",
@@ -82,9 +82,9 @@
82
82
  "open": "^10.2.0",
83
83
  "p-limit": "^6.1.0",
84
84
  "pi-footer": "0.5.1",
85
- "pi-lens": "4.1.0",
85
+ "pi-lens": "4.1.1",
86
86
  "pi-mcp-adapter": "2.27.0",
87
- "pi-web-access": "0.24.0",
87
+ "pi-web-access": "0.24.1",
88
88
  "pidusage": "^4.0.1",
89
89
  "promise.try": "^2.0.1",
90
90
  "qrcode-terminal": "^0.12.0",