bailian-cli-core 1.0.0-beta.0 → 1.0.1

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/dist/index.mjs CHANGED
@@ -1,6 +1,8 @@
1
- import { existsSync, mkdirSync, readFileSync, renameSync, statSync, writeFileSync } from "fs";
1
+ import { r as __toESM } from "./chunk-e9Ob2GDo.mjs";
2
+ import { appendFileSync, existsSync, mkdirSync, readFileSync, renameSync, statSync, unlinkSync, writeFileSync } from "fs";
2
3
  import { homedir } from "os";
3
4
  import { basename, join } from "path";
5
+ import { stringify } from "yaml";
4
6
  import { createHash, createHmac, randomUUID } from "crypto";
5
7
  //#region src/errors/codes.ts
6
8
  const ExitCode = {
@@ -15,42 +17,56 @@ const ExitCode = {
15
17
  };
16
18
  //#endregion
17
19
  //#region src/errors/base.ts
18
- /**
19
- * Base error class for the Bailian SDK.
20
- *
21
- * Carries an `exitCode` (intended for CLI consumers to translate into a
22
- * process exit code) and an optional `hint` describing how to recover.
23
- * SDK consumers may ignore `exitCode` and treat instances as ordinary errors.
24
- */
25
20
  var BailianError = class extends Error {
26
21
  exitCode;
27
22
  hint;
28
- constructor(message, exitCode = ExitCode.GENERAL, hint) {
29
- super(message);
23
+ api;
24
+ constructor(message, exitCode = ExitCode.GENERAL, hint, options) {
25
+ super(message, options?.cause !== void 0 ? { cause: options.cause } : void 0);
30
26
  this.name = "BailianError";
31
27
  this.exitCode = exitCode;
32
28
  this.hint = hint;
29
+ this.api = options?.api;
33
30
  }
34
31
  toJSON() {
32
+ const causeJson = serializeCause(this.cause);
35
33
  return { error: {
36
34
  code: this.exitCode,
37
35
  message: this.message,
38
- ...this.hint ? { hint: this.hint } : {}
36
+ ...this.hint ? { hint: this.hint } : {},
37
+ ...this.api?.httpStatus !== void 0 ? { http_status: this.api.httpStatus } : {},
38
+ ...this.api?.apiCode ? { api_code: this.api.apiCode } : {},
39
+ ...this.api?.requestId ? { request_id: this.api.requestId } : {},
40
+ ...causeJson ? { cause: causeJson } : {}
39
41
  } };
40
42
  }
41
43
  };
44
+ function serializeCause(cause) {
45
+ if (cause == null) return void 0;
46
+ if (cause instanceof Error) {
47
+ const out = { message: cause.message };
48
+ const code = cause.code;
49
+ if (code) out.code = code;
50
+ return out;
51
+ }
52
+ if (typeof cause === "string" || typeof cause === "number" || typeof cause === "boolean") return { message: String(cause) };
53
+ try {
54
+ return { message: JSON.stringify(cause) };
55
+ } catch {
56
+ return;
57
+ }
58
+ }
42
59
  //#endregion
43
60
  //#region src/errors/api.ts
44
61
  function mapApiError(status, body, _url) {
45
62
  const apiMsg = body.error?.message || body.message || `HTTP ${status}`;
46
- const apiCode = body.error?.type || body.code;
47
- if (status === 401 || status === 403 || apiCode === "InvalidApiKey" || apiCode === "Unauthorized") return new BailianError(`API key rejected (HTTP ${status}). ${apiMsg}`, ExitCode.AUTH, "Verify your API key is valid and not expired.");
48
- if (status === 429 || apiCode === "Throttling" || apiCode === "Throttling.RateQuota" || apiCode === "Throttling.AllocationQuota") return new BailianError(`Rate limit or quota exceeded. ${apiMsg}`, ExitCode.QUOTA, "Wait a moment and retry, or check your account quota.");
49
- if (status === 408 || status === 504) return new BailianError(`Request timed out (HTTP ${status}).`, ExitCode.TIMEOUT, "Try increasing the request timeout or retry later.");
50
- if (apiCode === "InvalidParameter" || apiCode === "BadRequest") return new BailianError(`Invalid parameter: ${apiMsg}`, ExitCode.USAGE);
51
- if (apiCode === "ModelNotFound" || apiCode === "AccessDenied") return new BailianError(`Model access denied or not found: ${apiMsg}`, ExitCode.AUTH, "Verify the model name and that your account has access to it.");
52
- if (apiCode === "DataInspectionFailed") return new BailianError(`Content flagged by safety filter: ${apiMsg}`, ExitCode.CONTENT_FILTER);
53
- return new BailianError(`API error: ${apiMsg} (HTTP ${status}${apiCode ? `, code: ${apiCode}` : ""})`, ExitCode.GENERAL);
63
+ const rawCode = body.error?.type ?? body.code;
64
+ const apiCode = typeof rawCode === "string" ? rawCode : typeof rawCode === "number" ? String(rawCode) : void 0;
65
+ return new BailianError(apiMsg, ExitCode.GENERAL, void 0, { api: {
66
+ httpStatus: status,
67
+ apiCode,
68
+ requestId: body.request_id
69
+ } });
54
70
  }
55
71
  //#endregion
56
72
  //#region src/config/schema.ts
@@ -76,6 +92,8 @@ function parseConfigFile(raw) {
76
92
  const obj = raw;
77
93
  const out = {};
78
94
  if (typeof obj.api_key === "string") out.api_key = obj.api_key;
95
+ if (typeof obj.access_token === "string" && obj.access_token.length > 0) out.access_token = obj.access_token;
96
+ else if (typeof obj.accessToken === "string" && obj.accessToken.length > 0) out.access_token = obj.accessToken;
79
97
  if (typeof obj.region === "string" && VALID_REGIONS.has(obj.region)) out.region = obj.region;
80
98
  if (typeof obj.base_url === "string" && obj.base_url.startsWith("http")) out.base_url = obj.base_url;
81
99
  if (typeof obj.output === "string" && VALID_OUTPUTS.has(obj.output)) out.output = obj.output;
@@ -89,6 +107,8 @@ function parseConfigFile(raw) {
89
107
  if (typeof obj.access_key_id === "string" && obj.access_key_id.length > 0) out.access_key_id = obj.access_key_id;
90
108
  if (typeof obj.access_key_secret === "string" && obj.access_key_secret.length > 0) out.access_key_secret = obj.access_key_secret;
91
109
  if (typeof obj.workspace_id === "string" && obj.workspace_id.length > 0) out.workspace_id = obj.workspace_id;
110
+ if (typeof obj.console_gateway_url === "string" && obj.console_gateway_url.startsWith("http")) out.console_gateway_url = obj.console_gateway_url;
111
+ if (typeof obj.telemetry === "boolean") out.telemetry = obj.telemetry;
92
112
  return out;
93
113
  }
94
114
  //#endregion
@@ -164,15 +184,21 @@ function formatErrorJson(code, message, hint) {
164
184
  } }, null, 2);
165
185
  }
166
186
  //#endregion
187
+ //#region src/output/yaml.ts
188
+ function formatYaml(data) {
189
+ return stringify(data).replace(/\n$/, "");
190
+ }
191
+ //#endregion
167
192
  //#region src/output/formatter.ts
168
193
  function detectOutputFormat(flagValue) {
169
- if (flagValue === "json" || flagValue === "text") return flagValue;
194
+ if (flagValue === "json" || flagValue === "text" || flagValue === "yaml") return flagValue;
170
195
  if (!process.stdout.isTTY) return "json";
171
196
  return "text";
172
197
  }
173
198
  function formatOutput(data, format) {
174
199
  switch (format) {
175
200
  case "json": return formatJson(data);
201
+ case "yaml": return formatYaml(data);
176
202
  case "text": return formatText(data);
177
203
  }
178
204
  }
@@ -200,6 +226,8 @@ function loadConfig(flags) {
200
226
  const file = readConfigFile();
201
227
  const apiKey = flags.apiKey || void 0;
202
228
  const fileApiKey = file.api_key;
229
+ const accessTokenEnv = process.env.DASHSCOPE_ACCESS_TOKEN?.trim() || void 0;
230
+ const fileAccessToken = file.access_token?.trim() || void 0;
203
231
  const explicitRegion = flags.region || process.env.DASHSCOPE_REGION || void 0;
204
232
  const cachedRegion = file.region;
205
233
  const region = explicitRegion || cachedRegion || "cn";
@@ -211,6 +239,8 @@ function loadConfig(flags) {
211
239
  if (!Number.isFinite(timeout) || timeout <= 0) throw new BailianError("Timeout must be a positive finite number.", ExitCode.USAGE);
212
240
  return {
213
241
  apiKey,
242
+ accessTokenEnv,
243
+ fileAccessToken,
214
244
  fileApiKey,
215
245
  fileRegion: file.region,
216
246
  configPath: getConfigPath(),
@@ -227,13 +257,15 @@ function loadConfig(flags) {
227
257
  accessKeyId: process.env.ALIBABA_CLOUD_ACCESS_KEY_ID || file.access_key_id || void 0,
228
258
  accessKeySecret: process.env.ALIBABA_CLOUD_ACCESS_KEY_SECRET || file.access_key_secret || void 0,
229
259
  workspaceId: process.env.BAILIAN_WORKSPACE_ID || file.workspace_id || void 0,
260
+ consoleGatewayUrl: process.env.BAILIAN_CONSOLE_GATEWAY_URL || file.console_gateway_url || "https://bailian-cs.console.aliyun.com",
230
261
  verbose: flags.verbose || process.env.DASHSCOPE_VERBOSE === "1",
231
262
  quiet: flags.quiet || false,
232
263
  noColor: flags.noColor || process.env.NO_COLOR !== void 0 || !process.stdout.isTTY,
233
264
  yes: flags.yes || false,
234
265
  dryRun: flags.dryRun || false,
235
266
  nonInteractive: flags.nonInteractive || false,
236
- async: flags.async || false
267
+ async: flags.async || false,
268
+ telemetry: process.env.DO_NOT_TRACK === "1" ? false : file.telemetry ?? true
237
269
  };
238
270
  }
239
271
  //#endregion
@@ -268,6 +300,7 @@ async function clearApiKey() {
268
300
  try {
269
301
  const existing = JSON.parse(readFileSync(path, "utf-8"));
270
302
  delete existing.api_key;
303
+ delete existing.access_token;
271
304
  const tmp = path + ".tmp";
272
305
  writeFileSync(tmp, JSON.stringify(existing, null, 2) + "\n", { mode: 384 });
273
306
  renameSync(tmp, path);
@@ -286,6 +319,16 @@ async function resolveCredential(config) {
286
319
  method: "api-key",
287
320
  source: "config.json"
288
321
  };
322
+ if (config.accessTokenEnv) return {
323
+ token: config.accessTokenEnv,
324
+ method: "access-token",
325
+ source: "DASHSCOPE_ACCESS_TOKEN"
326
+ };
327
+ if (config.fileAccessToken) return {
328
+ token: config.fileAccessToken,
329
+ method: "access-token",
330
+ source: "config.json"
331
+ };
289
332
  if (process.env.DASHSCOPE_API_KEY) return {
290
333
  token: process.env.DASHSCOPE_API_KEY,
291
334
  method: "api-key",
@@ -627,6 +670,54 @@ async function* parseSSE(response) {
627
670
  }
628
671
  }
629
672
  //#endregion
673
+ //#region src/console/gateway.ts
674
+ const GATEWAY_ACTION = "BroadScopeAspnGateway";
675
+ const GATEWAY_PRODUCT = "sfm_bailian";
676
+ function buildGatewayParams(api, data) {
677
+ return JSON.stringify({
678
+ Api: api,
679
+ V: "1.0",
680
+ Data: {
681
+ ...data,
682
+ cornerstoneParam: {
683
+ protocol: "V2",
684
+ console: "ONE_CONSOLE",
685
+ productCode: "p_efm",
686
+ consoleSite: "BAILIAN_ALIYUN",
687
+ ...typeof data.cornerstoneParam === "object" && data.cornerstoneParam !== null ? data.cornerstoneParam : {}
688
+ }
689
+ }
690
+ });
691
+ }
692
+ /**
693
+ * Invoke a Bailian **console** OpenAPI via the CLI gateway (`/cli/api.json`).
694
+ * Requires a console `access_token` (from `bl auth login --console`), not a DashScope API key.
695
+ */
696
+ async function callConsoleGateway(config, token, { api, data, region = "cn-beijing" }) {
697
+ const params = buildGatewayParams(api, data);
698
+ const body = new URLSearchParams({
699
+ params,
700
+ region
701
+ });
702
+ const timeoutMs = config.timeout * 1e3;
703
+ const gatewayBase = config.consoleGatewayUrl;
704
+ const res = await fetch(`${gatewayBase}/cli/api.json?action=${GATEWAY_ACTION}&product=${GATEWAY_PRODUCT}&api=${encodeURIComponent(api)}`, {
705
+ method: "POST",
706
+ headers: {
707
+ Accept: "*/*",
708
+ Authorization: `Bearer ${token}`,
709
+ "Content-Type": "application/x-www-form-urlencoded"
710
+ },
711
+ body: body.toString(),
712
+ signal: AbortSignal.timeout(timeoutMs)
713
+ });
714
+ if (!res.ok) {
715
+ const t = await res.text().catch(() => "");
716
+ throw new BailianError(`Console CLI gateway failed: HTTP ${res.status} ${res.statusText}`, ExitCode.GENERAL, t.slice(0, 500));
717
+ }
718
+ return res.json();
719
+ }
720
+ //#endregion
630
721
  //#region src/files/upload.ts
631
722
  /**
632
723
  * Upload local files to DashScope temporary OSS storage.
@@ -743,7 +834,7 @@ const GLOBAL_OPTIONS = [
743
834
  },
744
835
  {
745
836
  flag: "--output <format>",
746
- description: "Output format: text, json"
837
+ description: "Output format: text, json, yaml"
747
838
  },
748
839
  {
749
840
  flag: "--timeout <seconds>",
@@ -921,4 +1012,181 @@ function stripUndefined(obj) {
921
1012
  return obj;
922
1013
  }
923
1014
  //#endregion
924
- export { BAILIAN_HOST, BailianError, CHANNEL, DOCS_HOSTS, ExitCode, GLOBAL_OPTIONS, McpClient, REGIONS, SOURCE_CONFIG, TAGS, appCompletionEndpoint, chatEndpoint, clearApiKey, defineCommand, detectOutputFormat, ensureConfigDir, formatErrorJson, formatJson, formatKeyValue, formatOutput, formatTable, formatText, generateFilename, generateToolSchema, getConfigDir, getConfigPath, getCredentialsPath, imageEndpoint, imageSyncEndpoint, isCI, isInteractive, isLocalFile, loadApiKeyFromConfig, loadConfig, mapApiError, maskToken, mcpWebSearchEndpoint, memoryAddEndpoint, memoryListEndpoint, memoryNodeEndpoint, memorySearchEndpoint, parseConfigFile, parseSSE, profileSchemaEndpoint, readConfigFile, request, requestJson, resolveCredential, resolveFileUrl, resolveOutputDir, saveApiKeyToConfig, signRequest, speechRecognizeEndpoint, speechSynthesizeEndpoint, stripUndefined, taskEndpoint, trackingHeaders, uploadFile, userProfileEndpoint, videoGenerateEndpoint, writeConfigFile };
1015
+ //#region src/telemetry/event.ts
1016
+ function createTrackingEvent(opts) {
1017
+ const event = {
1018
+ command: opts.command,
1019
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1020
+ durationMs: opts.durationMs,
1021
+ success: opts.success,
1022
+ cliVersion: opts.cliVersion,
1023
+ region: opts.region,
1024
+ nodeVersion: process.version,
1025
+ os: process.platform
1026
+ };
1027
+ if (opts.authMethod) event.authMethod = opts.authMethod;
1028
+ if (!opts.success && opts.error) {
1029
+ if (opts.error.code !== void 0) event.errorCode = opts.error.code;
1030
+ if (opts.error.message) event.errorMessage = opts.error.message;
1031
+ }
1032
+ if (opts.params && Object.keys(opts.params).length > 0) event.params = opts.params;
1033
+ return event;
1034
+ }
1035
+ //#endregion
1036
+ //#region src/telemetry/sink.ts
1037
+ const TELEMETRY_FILE = () => join(getConfigDir(), "telemetry.jsonl");
1038
+ const MAX_FILE_SIZE = 5 * 1024 * 1024;
1039
+ let aesSendEvent;
1040
+ async function getAesSendEvent() {
1041
+ if (aesSendEvent) return aesSendEvent;
1042
+ try {
1043
+ const { default: AES } = await import("./index-node-C01JPicH.mjs").then((m) => /* @__PURE__ */ __toESM(m.default, 1));
1044
+ const { default: AESPluginEvent } = await import("./index-node-Ce966wQ9.mjs").then((m) => /* @__PURE__ */ __toESM(m.default, 1));
1045
+ aesSendEvent = new AES({ pid: "bailian-cli-node" }).use(AESPluginEvent);
1046
+ return aesSendEvent;
1047
+ } catch {
1048
+ return;
1049
+ }
1050
+ }
1051
+ async function localSink(event) {
1052
+ try {
1053
+ await ensureConfigDir();
1054
+ const path = TELEMETRY_FILE();
1055
+ try {
1056
+ if (statSync(path).size > MAX_FILE_SIZE) unlinkSync(path);
1057
+ } catch {}
1058
+ appendFileSync(path, JSON.stringify(event) + "\n");
1059
+ } catch {}
1060
+ }
1061
+ async function remoteSink(event) {
1062
+ try {
1063
+ const send = await getAesSendEvent();
1064
+ if (!send) return;
1065
+ const { command, params, ...ext } = event;
1066
+ send(command, {
1067
+ et: "EXP",
1068
+ ext,
1069
+ c1: params
1070
+ });
1071
+ } catch {}
1072
+ }
1073
+ //#endregion
1074
+ //#region src/telemetry/tracker.ts
1075
+ const GLOBAL_FLAG_KEYS = new Set([
1076
+ "apiKey",
1077
+ "baseUrl",
1078
+ "output",
1079
+ "quiet",
1080
+ "verbose",
1081
+ "timeout",
1082
+ "noColor",
1083
+ "yes",
1084
+ "dryRun",
1085
+ "help",
1086
+ "nonInteractive",
1087
+ "async",
1088
+ "region",
1089
+ "console"
1090
+ ]);
1091
+ /**
1092
+ * Allowlist of flag names safe to send to telemetry.
1093
+ *
1094
+ * Default is to NOT report. Only flags whose value space is enumerable / numeric / boolean
1095
+ * (and therefore cannot leak user content, credentials, file paths, URLs, or customer IDs)
1096
+ * belong here. When adding a new flag, ask: could this field carry PII, secrets, prompts,
1097
+ * file paths, URLs, or tenant identifiers? If yes, do NOT add it.
1098
+ */
1099
+ const PARAM_ALLOWLIST = new Set([
1100
+ "page",
1101
+ "pageSize",
1102
+ "n",
1103
+ "count",
1104
+ "model",
1105
+ "voice",
1106
+ "language",
1107
+ "provider",
1108
+ "capability",
1109
+ "temperature",
1110
+ "topP",
1111
+ "topK",
1112
+ "maxTokens",
1113
+ "seed",
1114
+ "stream",
1115
+ "size",
1116
+ "resolution",
1117
+ "ratio",
1118
+ "duration",
1119
+ "format",
1120
+ "audioFormat",
1121
+ "sampleRate",
1122
+ "pitch",
1123
+ "rate",
1124
+ "volume",
1125
+ "mode",
1126
+ "download",
1127
+ "noWait",
1128
+ "textOnly",
1129
+ "promptExtend",
1130
+ "noPromptExtend",
1131
+ "enableSsml",
1132
+ "watermark",
1133
+ "hasThoughts",
1134
+ "listTools",
1135
+ "rerank",
1136
+ "rerankTopN",
1137
+ "diarization"
1138
+ ]);
1139
+ function extractParams(flags) {
1140
+ const params = {};
1141
+ for (const [key, value] of Object.entries(flags)) {
1142
+ if (key.startsWith("_")) continue;
1143
+ if (GLOBAL_FLAG_KEYS.has(key)) continue;
1144
+ if (!PARAM_ALLOWLIST.has(key)) continue;
1145
+ if (value === void 0 || value === false) continue;
1146
+ params[key] = value;
1147
+ }
1148
+ return params;
1149
+ }
1150
+ async function trackCommandExecution(config, commandPath, flags, fn) {
1151
+ if (!config.telemetry) {
1152
+ await fn();
1153
+ return;
1154
+ }
1155
+ const start = performance.now();
1156
+ let success = true;
1157
+ let errorCode;
1158
+ let errorMessage;
1159
+ try {
1160
+ await fn();
1161
+ } catch (err) {
1162
+ success = false;
1163
+ if (err instanceof BailianError) {
1164
+ errorCode = err.exitCode;
1165
+ errorMessage = err.message;
1166
+ } else if (err instanceof Error) errorMessage = err.message;
1167
+ throw err;
1168
+ } finally {
1169
+ const durationMs = Math.round(performance.now() - start);
1170
+ let authMethod;
1171
+ if (config.apiKey) authMethod = "api-key";
1172
+ else if (config.fileApiKey) authMethod = "api-key";
1173
+ else if (config.accessTokenEnv || config.fileAccessToken) authMethod = "access-token";
1174
+ const event = createTrackingEvent({
1175
+ command: commandPath.join(" "),
1176
+ durationMs,
1177
+ success,
1178
+ error: success ? void 0 : {
1179
+ code: errorCode,
1180
+ message: errorMessage
1181
+ },
1182
+ cliVersion: config.clientVersion ?? "unknown",
1183
+ region: config.region,
1184
+ authMethod,
1185
+ params: extractParams(flags)
1186
+ });
1187
+ localSink(event).catch(() => {});
1188
+ remoteSink(event).catch(() => {});
1189
+ }
1190
+ }
1191
+ //#endregion
1192
+ export { BAILIAN_HOST, BailianError, CHANNEL, DOCS_HOSTS, ExitCode, GLOBAL_OPTIONS, McpClient, REGIONS, SOURCE_CONFIG, TAGS, appCompletionEndpoint, callConsoleGateway, chatEndpoint, clearApiKey, createTrackingEvent, defineCommand, detectOutputFormat, ensureConfigDir, formatErrorJson, formatJson, formatKeyValue, formatOutput, formatTable, formatText, formatYaml, generateFilename, generateToolSchema, getConfigDir, getConfigPath, getCredentialsPath, imageEndpoint, imageSyncEndpoint, isCI, isInteractive, isLocalFile, loadApiKeyFromConfig, loadConfig, localSink, mapApiError, maskToken, mcpWebSearchEndpoint, memoryAddEndpoint, memoryListEndpoint, memoryNodeEndpoint, memorySearchEndpoint, parseConfigFile, parseSSE, profileSchemaEndpoint, readConfigFile, remoteSink, request, requestJson, resolveCredential, resolveFileUrl, resolveOutputDir, saveApiKeyToConfig, signRequest, speechRecognizeEndpoint, speechSynthesizeEndpoint, stripUndefined, taskEndpoint, trackCommandExecution, trackingHeaders, uploadFile, userProfileEndpoint, videoGenerateEndpoint, writeConfigFile };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bailian-cli-core",
3
- "version": "1.0.0-beta.0",
3
+ "version": "1.0.1",
4
4
  "description": "Core SDK for bailian-cli. See https://www.npmjs.com/package/bailian-cli for usage.",
5
5
  "homepage": "https://bailian.console.aliyun.com/cli",
6
6
  "license": "Apache-2.0",
@@ -18,7 +18,12 @@
18
18
  "access": "public",
19
19
  "registry": "https://registry.npmjs.org/"
20
20
  },
21
+ "dependencies": {
22
+ "yaml": "^2.8.3"
23
+ },
21
24
  "devDependencies": {
25
+ "@ali/aes-tracker": "^3.3.18",
26
+ "@ali/aes-tracker-plugin-event": "^3.0.3",
22
27
  "@types/node": "^24",
23
28
  "@typescript/native-preview": "7.0.0-dev.20260328.1",
24
29
  "typescript": "^6.0.2",
@@ -27,6 +32,14 @@
27
32
  "engines": {
28
33
  "node": ">=22.12.0"
29
34
  },
35
+ "inlinedDependencies": {
36
+ "@ali/aes-tracker": "3.3.18",
37
+ "@ali/aes-tracker-plugin-event": "3.0.3",
38
+ "node-fetch": "2.7.0",
39
+ "tr46": "0.0.3",
40
+ "webidl-conversions": "3.0.1",
41
+ "whatwg-url": "5.0.0"
42
+ },
30
43
  "scripts": {
31
44
  "build": "vp pack",
32
45
  "dev": "vp pack --watch",