tirtc-devtools-cli 0.0.3 → 0.0.5
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# TiRTC
|
|
1
|
+
# TiRTC DevTools CLI
|
|
2
2
|
|
|
3
|
-
`devtools/cli/` 承接 `TiRTC
|
|
3
|
+
`devtools/cli/` 承接 `TiRTC DevTools CLI`。它是当前唯一用户界面的产品门面 (command client),为用户提供了一套操作 `Matrix Host` 控制面的终端工具。
|
|
4
4
|
|
|
5
5
|
## 使用说明 (C-Lite)
|
|
6
6
|
|
package/USAGE.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# TiRTC
|
|
1
|
+
# TiRTC DevTools CLI(用户手册)
|
|
2
2
|
|
|
3
3
|
`tirtc-devtools-cli` 是基于 `app-server/host` 的命令行门面,用来控制会话、服务、连接、流、命令通道与调试工具。
|
|
4
4
|
|
|
@@ -118,27 +118,46 @@ node devtools/cli/bin/tirtc-devtools-cli.js --help
|
|
|
118
118
|
|
|
119
119
|
用途:直接签发 token,并把调试所需的核心信息一次性输出到控制台,同时在本地生成二维码 PNG。
|
|
120
120
|
|
|
121
|
-
- `tirtc-devtools-cli token issue <
|
|
121
|
+
- `tirtc-devtools-cli token issue <peerId>`:签发 token;默认从环境变量读取凭据,并使用 `peerId` 作为默认 `localId`。
|
|
122
122
|
|
|
123
123
|
可选参数:
|
|
124
124
|
|
|
125
|
+
- `--access-id <accessId>`:显式传 access id;不传时读取 `TIRTC_CONN_ACCESS_ID`。
|
|
126
|
+
- `--secret-key <secretKey>`:显式传 secret key;不传时读取 `TIRTC_CONN_SECRET_KEY`。
|
|
127
|
+
- `--local-id <localId>`:显式传 local id;不传时默认使用 `peerId`。
|
|
125
128
|
- `--openapi-entry <url>`:覆盖默认 openapi entry。
|
|
126
129
|
- `--service-entry <entry>`:附加到组合 payload,便于扫码后直接连到目标服务。
|
|
127
130
|
- `--user-ttl-seconds <seconds>`:覆盖 user token ttl。
|
|
128
131
|
- `--channel-ttl-seconds <seconds>`:覆盖 channel token ttl。
|
|
132
|
+
- `--qr-error-correction-level <level>`:设置二维码纠错级别,可选 `L/M/Q/H`,默认 `M`。
|
|
133
|
+
- `--ascii-max-columns <columns>`:显式限制 ASCII 二维码最大宽度;不传时优先读取当前终端宽度或 `COLUMNS`。
|
|
129
134
|
|
|
130
135
|
默认输出四部分:
|
|
131
136
|
|
|
132
137
|
- `Issued Token Summary`:面向人类阅读的摘要,快速确认 local/peer/entry/ttl。
|
|
133
138
|
- `Token`:单独罗列 token 字符串,便于复制粘贴。
|
|
134
139
|
- `Payload JSON`:组合 JSON,对方或 Agent 可直接复制使用。
|
|
140
|
+
- `QR Code ASCII`:非 `--json` 模式下尽量输出 ASCII 二维码;如果终端宽度不足,会提示改看 PNG。
|
|
135
141
|
- `QR Code PNG`:基于组合 JSON 生成的本地 PNG 文件绝对路径。适合人工打开图片后扫码。
|
|
136
142
|
|
|
137
143
|
说明:
|
|
138
144
|
|
|
139
145
|
- 组合 JSON 的 `type` 固定为 `tirtc-connect-token`。
|
|
140
146
|
- PNG 二维码承载的是组合 JSON,不再暴露 `secret_key`。
|
|
141
|
-
-
|
|
147
|
+
- CLI 不会在最终 JSON、控制台输出或二维码内容里打印 `access_id` 和 `secret_key`。
|
|
148
|
+
- `--json` 模式下只输出 `payload`、`payloadJson`、`token` 和 `qrCodePngPath`,不输出 ASCII 二维码。
|
|
149
|
+
- 如果觉得 ASCII 二维码太密,可把纠错级别从默认 `M` 调低到 `L`;如果希望更耐遮挡,可调高到 `Q` 或 `H`,但二维码会更密。
|
|
150
|
+
|
|
151
|
+
推荐默认做法:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
export TIRTC_CONN_ACCESS_ID="<ACCESS_ID>"
|
|
155
|
+
export TIRTC_CONN_SECRET_KEY="<SECRET_KEY>"
|
|
156
|
+
tirtc-devtools-cli --json token issue "<PEER_ID>"
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
只有在你明确需要覆盖环境变量时,才额外传 `--access-id` 和 `--secret-key`。
|
|
160
|
+
只有在你明确需要不同的本地用户标识时,才额外传 `--local-id`。
|
|
142
161
|
|
|
143
162
|
### Stream
|
|
144
163
|
|
|
@@ -31,9 +31,12 @@ function printQuickstartGuide() {
|
|
|
31
31
|
' - 持 token + peer_id 连接服务端;连接成功后请求 audio_stream_id/video_stream_id',
|
|
32
32
|
'',
|
|
33
33
|
'额外:如果你只想本地直接签发并拿到可扫码 payload,可执行:',
|
|
34
|
-
'
|
|
34
|
+
' export TIRTC_CONN_ACCESS_ID="<ACCESS_ID>"',
|
|
35
|
+
' export TIRTC_CONN_SECRET_KEY="<SECRET_KEY>"',
|
|
36
|
+
' node devtools/cli/bin/tirtc-devtools-cli.js token issue "<LOCAL_ID>" "<PEER_ID>" \\',
|
|
35
37
|
' --service-entry "<SERVICE_ENTRY>"',
|
|
36
38
|
' # CLI 会直接打印摘要、payload JSON、token,并生成本地二维码 PNG 路径',
|
|
39
|
+
' # 如需临时覆盖,也可显式传 --access-id / --secret-key',
|
|
37
40
|
'',
|
|
38
41
|
'步骤 5:服务端观测',
|
|
39
42
|
' node devtools/cli/bin/tirtc-devtools-cli.js --session <SESSION_ID> events tail',
|
|
@@ -9,9 +9,11 @@ const session_manager_1 = require("./session_manager");
|
|
|
9
9
|
const media_assets_1 = require("./media_assets");
|
|
10
10
|
const transport_1 = require("./transport");
|
|
11
11
|
const token_tool_1 = require("./token_tool");
|
|
12
|
-
const CLI_VERSION = '0.0.
|
|
12
|
+
const CLI_VERSION = '0.0.5';
|
|
13
13
|
const HOST_VERSION = '1.0.0';
|
|
14
14
|
const PROTOCOL_VERSION = '1.0.0';
|
|
15
|
+
const kTokenIssueAccessIdEnvVar = 'TIRTC_CONN_ACCESS_ID';
|
|
16
|
+
const kTokenIssueSecretKeyEnvVar = 'TIRTC_CONN_SECRET_KEY';
|
|
15
17
|
if (process.argv.includes('--version') || process.argv.includes('-V')) {
|
|
16
18
|
console.log('CLI Version: ' + CLI_VERSION);
|
|
17
19
|
console.log('Host Version: ' + HOST_VERSION);
|
|
@@ -325,6 +327,60 @@ async function runTokenIssue(params, options) {
|
|
|
325
327
|
return exitCode;
|
|
326
328
|
}
|
|
327
329
|
}
|
|
330
|
+
async function runTokenIssueFromCli(peerId, commandOptions, options) {
|
|
331
|
+
const parsePositiveInt = (name, raw) => {
|
|
332
|
+
if (raw === undefined) {
|
|
333
|
+
return undefined;
|
|
334
|
+
}
|
|
335
|
+
const parsed = Number.parseInt(raw, 10);
|
|
336
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
337
|
+
throw new Error(name + ' must be a positive integer');
|
|
338
|
+
}
|
|
339
|
+
return parsed;
|
|
340
|
+
};
|
|
341
|
+
const parseQrErrorCorrectionLevel = (raw) => {
|
|
342
|
+
if (raw === undefined) {
|
|
343
|
+
return undefined;
|
|
344
|
+
}
|
|
345
|
+
const normalized = raw.trim().toUpperCase();
|
|
346
|
+
if (normalized !== 'L' && normalized !== 'M' && normalized !== 'Q' && normalized !== 'H') {
|
|
347
|
+
throw new Error('qr-error-correction-level must be one of: L, M, Q, H');
|
|
348
|
+
}
|
|
349
|
+
return normalized;
|
|
350
|
+
};
|
|
351
|
+
try {
|
|
352
|
+
const accessId = resolveTokenIssueCredential(commandOptions.accessId, kTokenIssueAccessIdEnvVar, '--access-id');
|
|
353
|
+
const secretKey = resolveTokenIssueCredential(commandOptions.secretKey, kTokenIssueSecretKeyEnvVar, '--secret-key');
|
|
354
|
+
return await runTokenIssue({
|
|
355
|
+
accessId,
|
|
356
|
+
secretKey,
|
|
357
|
+
localId: commandOptions.localId?.trim() || peerId,
|
|
358
|
+
peerId,
|
|
359
|
+
openapiEntry: commandOptions.openapiEntry,
|
|
360
|
+
serviceEntry: commandOptions.serviceEntry,
|
|
361
|
+
userTtlSeconds: parsePositiveInt('user-ttl-seconds', commandOptions.userTtlSeconds),
|
|
362
|
+
channelTtlSeconds: parsePositiveInt('channel-ttl-seconds', commandOptions.channelTtlSeconds),
|
|
363
|
+
qrErrorCorrectionLevel: parseQrErrorCorrectionLevel(commandOptions.qrErrorCorrectionLevel),
|
|
364
|
+
asciiMaxColumns: parsePositiveInt('ascii-max-columns', commandOptions.asciiMaxColumns),
|
|
365
|
+
}, options);
|
|
366
|
+
}
|
|
367
|
+
catch (error) {
|
|
368
|
+
const normalized = normalizeError(error);
|
|
369
|
+
const reasonCode = normalized.reasonCode;
|
|
370
|
+
const exitCode = facade_1.ErrorReasonCodeMapping[reasonCode] ?? 1;
|
|
371
|
+
if (options.json) {
|
|
372
|
+
console.log(JSON.stringify({
|
|
373
|
+
code: exitCode,
|
|
374
|
+
message: normalized.message,
|
|
375
|
+
data: normalized.data,
|
|
376
|
+
}));
|
|
377
|
+
}
|
|
378
|
+
else {
|
|
379
|
+
console.error('Error (' + reasonCode + '): ' + normalized.message);
|
|
380
|
+
}
|
|
381
|
+
return exitCode;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
328
384
|
function runAndExit(promise) {
|
|
329
385
|
promise.then((code) => {
|
|
330
386
|
process.exit(code);
|
|
@@ -339,6 +395,18 @@ function mustParseJsonPayload(input) {
|
|
|
339
395
|
throw new Error('payloadJson must be valid JSON text');
|
|
340
396
|
}
|
|
341
397
|
}
|
|
398
|
+
function resolveTokenIssueCredential(explicitValue, envVarName, optionName) {
|
|
399
|
+
const normalizedExplicit = explicitValue?.trim();
|
|
400
|
+
if (normalizedExplicit) {
|
|
401
|
+
return normalizedExplicit;
|
|
402
|
+
}
|
|
403
|
+
const normalizedEnv = process.env[envVarName]?.trim();
|
|
404
|
+
if (normalizedEnv) {
|
|
405
|
+
return normalizedEnv;
|
|
406
|
+
}
|
|
407
|
+
throw new Error('missing credential: set environment variable ' + envVarName +
|
|
408
|
+
' or pass ' + optionName + ' explicitly');
|
|
409
|
+
}
|
|
342
410
|
program.name('tirtc-devtools-cli')
|
|
343
411
|
.description('TiRTC DevTools CLI')
|
|
344
412
|
.option('--config <path>', '配置文件路径(TOML)')
|
|
@@ -545,33 +613,19 @@ connection.command('disconnect').description('断开当前连接').action(() =>
|
|
|
545
613
|
runAndExit(runCommand('connection disconnect', {}, getCliOptions()));
|
|
546
614
|
});
|
|
547
615
|
const token = program.command('token').description('Token 工具:签发 token,并输出可直接使用的 JSON 与本地二维码 PNG');
|
|
548
|
-
token.command('issue <
|
|
549
|
-
.description('
|
|
616
|
+
token.command('issue <peer_id>')
|
|
617
|
+
.description('默认从环境变量读取 access/secret,并以 peer_id 作为默认 local_id 来签发 token')
|
|
618
|
+
.option('--access-id <accessId>', '显式 access id;不传时读取 ' + kTokenIssueAccessIdEnvVar)
|
|
619
|
+
.option('--secret-key <secretKey>', '显式 secret key;不传时读取 ' + kTokenIssueSecretKeyEnvVar)
|
|
620
|
+
.option('--local-id <localId>', '显式 local id;不传时默认使用 peer_id')
|
|
550
621
|
.option('--openapi-entry <url>', '可选 openapi entry;留空时走 runtime 默认值')
|
|
551
622
|
.option('--service-entry <entry>', '可选 service entry;用于组合连接 payload 与二维码 PNG')
|
|
552
623
|
.option('--user-ttl-seconds <seconds>', '可选 user token ttl(秒)')
|
|
553
624
|
.option('--channel-ttl-seconds <seconds>', '可选 channel token ttl(秒)')
|
|
554
|
-
.
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
}
|
|
559
|
-
const parsed = Number.parseInt(raw, 10);
|
|
560
|
-
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
561
|
-
throw new Error(name + ' must be a positive integer');
|
|
562
|
-
}
|
|
563
|
-
return parsed;
|
|
564
|
-
};
|
|
565
|
-
runAndExit(runTokenIssue({
|
|
566
|
-
accessId: access_id,
|
|
567
|
-
secretKey: secret_key,
|
|
568
|
-
localId: local_id,
|
|
569
|
-
peerId: peer_id,
|
|
570
|
-
openapiEntry: commandOptions.openapiEntry,
|
|
571
|
-
serviceEntry: commandOptions.serviceEntry,
|
|
572
|
-
userTtlSeconds: parsePositiveInt('user-ttl-seconds', commandOptions.userTtlSeconds),
|
|
573
|
-
channelTtlSeconds: parsePositiveInt('channel-ttl-seconds', commandOptions.channelTtlSeconds),
|
|
574
|
-
}, getCliOptions()));
|
|
625
|
+
.option('--qr-error-correction-level <level>', '二维码纠错级别:L/M/Q/H;默认 M')
|
|
626
|
+
.option('--ascii-max-columns <columns>', 'ASCII 二维码最大宽度;不传时优先读取当前终端宽度或 COLUMNS')
|
|
627
|
+
.action((peer_id, commandOptions) => {
|
|
628
|
+
runAndExit(runTokenIssueFromCli(peer_id, commandOptions, getCliOptions()));
|
|
575
629
|
});
|
|
576
630
|
const stream = program.command('stream').description('流控制:发送/接收/请求策略');
|
|
577
631
|
stream.command('list').description('查看流快照列表').action(() => {
|
|
@@ -7,6 +7,8 @@ export type TokenIssueInput = {
|
|
|
7
7
|
serviceEntry?: string;
|
|
8
8
|
userTtlSeconds?: number;
|
|
9
9
|
channelTtlSeconds?: number;
|
|
10
|
+
qrErrorCorrectionLevel?: 'L' | 'M' | 'Q' | 'H';
|
|
11
|
+
asciiMaxColumns?: number;
|
|
10
12
|
};
|
|
11
13
|
export type IssuedTokenPayload = {
|
|
12
14
|
version: 1;
|
|
@@ -26,10 +28,11 @@ export type TokenIssueOutput = {
|
|
|
26
28
|
token: string;
|
|
27
29
|
qrCodePngPath: string;
|
|
28
30
|
qrCodeAscii: string;
|
|
31
|
+
qrCodeAsciiIncluded: boolean;
|
|
29
32
|
};
|
|
30
33
|
export declare function issueToken(input: TokenIssueInput): Promise<string>;
|
|
31
34
|
export declare function buildIssuedTokenPayload(input: TokenIssueInput, token: string): IssuedTokenPayload;
|
|
32
|
-
export declare function writePngQrcode(payloadJson: string, outputPath: string): Promise<string>;
|
|
33
|
-
export declare function buildAsciiQrcode(payloadJson: string): Promise<string>;
|
|
35
|
+
export declare function writePngQrcode(payloadJson: string, outputPath: string, errorCorrectionLevel: 'L' | 'M' | 'Q' | 'H'): Promise<string>;
|
|
36
|
+
export declare function buildAsciiQrcode(payloadJson: string, terminalColumns?: number, errorCorrectionLevel?: 'L' | 'M' | 'Q' | 'H'): Promise<string>;
|
|
34
37
|
export declare function issueTokenWithQrcode(input: TokenIssueInput): Promise<TokenIssueOutput>;
|
|
35
38
|
export declare function formatTokenIssueConsoleOutput(output: TokenIssueOutput): string;
|
|
@@ -17,8 +17,6 @@ const embedded_paths_1 = require("./embedded_paths");
|
|
|
17
17
|
const kDefaultOpenapiEntry = 'http://api-test-tirtc.tange365.com';
|
|
18
18
|
const kDefaultUserTtlSeconds = 86400;
|
|
19
19
|
const kDefaultChannelTtlSeconds = 300;
|
|
20
|
-
const kAsciiQrBlack = '██';
|
|
21
|
-
const kAsciiQrWhite = ' ';
|
|
22
20
|
const kAsciiQrQuietZoneModules = 2;
|
|
23
21
|
function pathExists(filePath) {
|
|
24
22
|
return fs_1.default.existsSync(filePath);
|
|
@@ -176,6 +174,58 @@ function issueTokenViaSubprocess(addonPath, input) {
|
|
|
176
174
|
function readQrModule(modules, row, column) {
|
|
177
175
|
return Boolean(modules.data[row * modules.size + column]);
|
|
178
176
|
}
|
|
177
|
+
function resolveTerminalColumns() {
|
|
178
|
+
if (typeof process.stdout.columns === 'number' && Number.isInteger(process.stdout.columns) && process.stdout.columns > 0) {
|
|
179
|
+
return process.stdout.columns;
|
|
180
|
+
}
|
|
181
|
+
const raw = process.env.COLUMNS;
|
|
182
|
+
if (!raw) {
|
|
183
|
+
return undefined;
|
|
184
|
+
}
|
|
185
|
+
const parsed = Number.parseInt(raw, 10);
|
|
186
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
187
|
+
return undefined;
|
|
188
|
+
}
|
|
189
|
+
return parsed;
|
|
190
|
+
}
|
|
191
|
+
function resolveAsciiMaxColumns(explicit) {
|
|
192
|
+
if (explicit !== undefined) {
|
|
193
|
+
return explicit;
|
|
194
|
+
}
|
|
195
|
+
return resolveTerminalColumns();
|
|
196
|
+
}
|
|
197
|
+
function readQrModuleOrWhite(modules, row, column) {
|
|
198
|
+
if (row < 0 || column < 0 || row >= modules.size || column >= modules.size) {
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
return readQrModule(modules, row, column);
|
|
202
|
+
}
|
|
203
|
+
function renderHalfBlockQr(modules) {
|
|
204
|
+
const lines = [];
|
|
205
|
+
const start = -kAsciiQrQuietZoneModules;
|
|
206
|
+
const end = modules.size + kAsciiQrQuietZoneModules;
|
|
207
|
+
for (let row = start; row < end; row += 2) {
|
|
208
|
+
let line = '';
|
|
209
|
+
for (let column = start; column < end; column += 1) {
|
|
210
|
+
const top = readQrModuleOrWhite(modules, row, column);
|
|
211
|
+
const bottom = readQrModuleOrWhite(modules, row + 1, column);
|
|
212
|
+
if (top && bottom) {
|
|
213
|
+
line += '█';
|
|
214
|
+
}
|
|
215
|
+
else if (top) {
|
|
216
|
+
line += '▀';
|
|
217
|
+
}
|
|
218
|
+
else if (bottom) {
|
|
219
|
+
line += '▄';
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
line += ' ';
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
lines.push(line);
|
|
226
|
+
}
|
|
227
|
+
return lines.join('\n');
|
|
228
|
+
}
|
|
179
229
|
async function issueToken(input) {
|
|
180
230
|
const runtimePlatform = resolveRuntimePlatform();
|
|
181
231
|
const runtimeBundleRoot = resolveRuntimeBundleRoot(runtimePlatform);
|
|
@@ -204,50 +254,45 @@ function buildIssuedTokenPayload(input, token) {
|
|
|
204
254
|
generated_at: new Date().toISOString(),
|
|
205
255
|
};
|
|
206
256
|
}
|
|
207
|
-
async function writePngQrcode(payloadJson, outputPath) {
|
|
257
|
+
async function writePngQrcode(payloadJson, outputPath, errorCorrectionLevel) {
|
|
208
258
|
const resolvedPath = path_1.default.resolve(outputPath);
|
|
209
259
|
ensureDirectory(path_1.default.dirname(resolvedPath));
|
|
210
260
|
await qrcode_1.default.toFile(resolvedPath, payloadJson, {
|
|
211
|
-
errorCorrectionLevel
|
|
261
|
+
errorCorrectionLevel,
|
|
212
262
|
margin: 2,
|
|
213
263
|
type: 'png',
|
|
214
264
|
width: 960,
|
|
215
265
|
});
|
|
216
266
|
return resolvedPath;
|
|
217
267
|
}
|
|
218
|
-
async function buildAsciiQrcode(payloadJson) {
|
|
219
|
-
const qr = qrcode_1.default.create(payloadJson, { errorCorrectionLevel
|
|
220
|
-
const
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
268
|
+
async function buildAsciiQrcode(payloadJson, terminalColumns, errorCorrectionLevel = 'M') {
|
|
269
|
+
const qr = qrcode_1.default.create(payloadJson, { errorCorrectionLevel });
|
|
270
|
+
const requiredColumns = qr.modules.size + (kAsciiQrQuietZoneModules * 2);
|
|
271
|
+
const availableColumns = resolveAsciiMaxColumns(terminalColumns);
|
|
272
|
+
if (availableColumns !== undefined && availableColumns < requiredColumns) {
|
|
273
|
+
return [
|
|
274
|
+
'(omitted: terminal width too narrow for ASCII QR)',
|
|
275
|
+
`required_columns: ${requiredColumns}`,
|
|
276
|
+
`available_columns: ${availableColumns}`,
|
|
277
|
+
'open the QR Code PNG path below instead.',
|
|
278
|
+
].join('\n');
|
|
225
279
|
}
|
|
226
|
-
|
|
227
|
-
let line = quietZone;
|
|
228
|
-
for (let column = 0; column < qr.modules.size; column += 1) {
|
|
229
|
-
line += readQrModule(qr.modules, row, column) ? kAsciiQrBlack : kAsciiQrWhite;
|
|
230
|
-
}
|
|
231
|
-
line += quietZone;
|
|
232
|
-
lines.push(line);
|
|
233
|
-
}
|
|
234
|
-
for (let index = 0; index < kAsciiQrQuietZoneModules; index += 1) {
|
|
235
|
-
lines.push(fullQuietLine);
|
|
236
|
-
}
|
|
237
|
-
return lines.join('\n');
|
|
280
|
+
return renderHalfBlockQr(qr.modules);
|
|
238
281
|
}
|
|
239
282
|
async function issueTokenWithQrcode(input) {
|
|
240
283
|
const token = await issueToken(input);
|
|
241
284
|
const payload = buildIssuedTokenPayload(input, token);
|
|
242
285
|
const payloadJson = JSON.stringify(payload);
|
|
243
|
-
const
|
|
244
|
-
const
|
|
286
|
+
const qrErrorCorrectionLevel = input.qrErrorCorrectionLevel ?? 'M';
|
|
287
|
+
const qrCodePngPath = await writePngQrcode(payloadJson, buildQrCodePngPath(payload), qrErrorCorrectionLevel);
|
|
288
|
+
const qrCodeAscii = await buildAsciiQrcode(payloadJson, input.asciiMaxColumns, qrErrorCorrectionLevel);
|
|
245
289
|
return {
|
|
246
290
|
payload,
|
|
247
291
|
payloadJson,
|
|
248
292
|
token,
|
|
249
293
|
qrCodePngPath,
|
|
250
294
|
qrCodeAscii,
|
|
295
|
+
qrCodeAsciiIncluded: !qrCodeAscii.startsWith('(omitted:'),
|
|
251
296
|
};
|
|
252
297
|
}
|
|
253
298
|
function formatTokenIssueConsoleOutput(output) {
|