@ricsam/r5dctl 0.0.51 → 0.0.54

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
@@ -14,9 +14,13 @@ npm install -g @ricsam/r5dctl
14
14
  r5dctl auth login
15
15
  ```
16
16
 
17
- This starts a device-style flow and opens:
17
+ This starts a device-authorization flow, prints an eight-character code and QR,
18
+ and opens `https://r5d.dev/auth`. Approve the matching device details in the
19
+ browser before the credential is saved.
18
20
 
19
- - `https://r5d.dev/r5dctl/login/<requestId>`
21
+ Use `--no-open` when no host browser is available, `--no-qr` to print only the
22
+ short code, `--device-name <name>` to label the authorized device, and
23
+ `--worker-label <label>` to record the worker it is intended to run.
20
24
 
21
25
  Credentials are stored in:
22
26
 
package/dist/cjs/cli.cjs CHANGED
@@ -28,6 +28,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
28
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
29
  var cli_exports = {};
30
30
  __export(cli_exports, {
31
+ advanceTransientDevicePollBackoff: () => advanceTransientDevicePollBackoff,
31
32
  collectK8sUsage: () => collectK8sUsage,
32
33
  formatK8sCpu: () => formatK8sCpu,
33
34
  formatK8sMemory: () => formatK8sMemory,
@@ -36,6 +37,8 @@ __export(cli_exports, {
36
37
  getK8sUsageState: () => getK8sUsageState,
37
38
  getProjectEnvValue: () => getProjectEnvValue,
38
39
  getR5dctlVersion: () => getR5dctlVersion,
40
+ getTransientDevicePollDelay: () => getTransientDevicePollDelay,
41
+ handleAuthLogin: () => handleAuthLogin,
39
42
  main: () => main,
40
43
  parseAnswerFlags: () => parseAnswerFlags,
41
44
  parseConversationRenderArgs: () => parseConversationRenderArgs,
@@ -55,6 +58,7 @@ __export(cli_exports, {
55
58
  renderConversationOverviewResponse: () => renderConversationOverviewResponse,
56
59
  renderConversationResponse: () => renderConversationResponse,
57
60
  renderConversationWorkResponse: () => renderConversationWorkResponse,
61
+ renderDeviceAuthorizationPrompt: () => renderDeviceAuthorizationPrompt,
58
62
  renderEnvValues: () => renderEnvValues,
59
63
  renderK8sUsageReport: () => renderK8sUsageReport,
60
64
  renderProcessHistory: () => renderProcessHistory,
@@ -64,13 +68,16 @@ __export(cli_exports, {
64
68
  renderWorkspaceSync: () => renderWorkspaceSync,
65
69
  resolveCommandExecution: () => resolveCommandExecution,
66
70
  runR5dctlCli: () => runR5dctlCli,
67
- summarizeEnvData: () => summarizeEnvData
71
+ summarizeEnvData: () => summarizeEnvData,
72
+ writeConfig: () => writeConfig
68
73
  });
69
74
  module.exports = __toCommonJS(cli_exports);
70
75
  var import_node_fs = __toESM(require("node:fs"), 1);
71
76
  var import_node_os = __toESM(require("node:os"), 1);
72
77
  var import_node_path = __toESM(require("node:path"), 1);
73
78
  var import_node_child_process = require("node:child_process");
79
+ var import_node_crypto = require("node:crypto");
80
+ var import_qrcode = __toESM(require("qrcode"), 1);
74
81
  var import_promises = require("node:timers/promises");
75
82
  var import_dotenv = require("dotenv");
76
83
  var import_r5d_api = require("@ricsam/r5d-api");
@@ -105,7 +112,7 @@ const CLI_GLOBAL_OPTION_HELP = [
105
112
  const CLI_ONLY_HELP_ENTRIES = [
106
113
  {
107
114
  section: "auth",
108
- usage: "auth login [--no-open]",
115
+ usage: "auth login [--no-open] [--no-qr] [--device-name <name>] [--worker-label <label>]",
109
116
  description: "Start the browser login flow."
110
117
  },
111
118
  {
@@ -117,7 +124,7 @@ const CLI_ONLY_HELP_ENTRIES = [
117
124
  const CLI_ONLY_COMMAND_HELP = [
118
125
  {
119
126
  path: ["auth", "login"],
120
- usage: "auth login [--no-open]"
127
+ usage: "auth login [--no-open] [--no-qr] [--device-name <name>] [--worker-label <label>]"
121
128
  },
122
129
  {
123
130
  path: ["shell"],
@@ -449,6 +456,21 @@ function parseOptionalFlagValue(args, flag, shortFlag) {
449
456
  function hasBooleanFlag(args, flag) {
450
457
  return args.includes(flag);
451
458
  }
459
+ function assertAuthLoginArgs(args) {
460
+ const valueFlags = /* @__PURE__ */ new Set(["--device-name", "--worker-label"]);
461
+ const booleanFlags = /* @__PURE__ */ new Set(["--no-open", "--no-qr"]);
462
+ for (let index = 0; index < args.length; index += 1) {
463
+ const arg = args[index];
464
+ if (booleanFlags.has(arg)) continue;
465
+ if (valueFlags.has(arg)) {
466
+ if (!args[index + 1]) throw new Error(`Missing value for ${arg}`);
467
+ index += 1;
468
+ continue;
469
+ }
470
+ if ([...valueFlags].some((flag) => arg.startsWith(`${flag}=`))) continue;
471
+ throw new Error(`Unknown auth login flag: ${arg}`);
472
+ }
473
+ }
452
474
  function parseGlobalArgs(argv) {
453
475
  const options = {
454
476
  json: false,
@@ -580,12 +602,27 @@ function serializeConfig(config) {
580
602
  function writeConfig(configPath, config) {
581
603
  const dir = import_node_path.default.dirname(configPath);
582
604
  import_node_fs.default.mkdirSync(dir, { recursive: true, mode: 448 });
605
+ import_node_fs.default.chmodSync(dir, 448);
583
606
  const serialized = serializeConfig(config);
584
- import_node_fs.default.writeFileSync(configPath, `${JSON.stringify(serialized, null, 2)}
585
- `, {
586
- mode: 384
587
- });
588
- import_node_fs.default.chmodSync(configPath, 384);
607
+ const tempPath = import_node_path.default.join(dir, `.${import_node_path.default.basename(configPath)}.${process.pid}.${(0, import_node_crypto.randomUUID)()}.tmp`);
608
+ let descriptor;
609
+ try {
610
+ descriptor = import_node_fs.default.openSync(tempPath, "wx", 384);
611
+ import_node_fs.default.writeFileSync(descriptor, `${JSON.stringify(serialized, null, 2)}
612
+ `, "utf8");
613
+ import_node_fs.default.fsyncSync(descriptor);
614
+ import_node_fs.default.closeSync(descriptor);
615
+ descriptor = void 0;
616
+ import_node_fs.default.renameSync(tempPath, configPath);
617
+ import_node_fs.default.chmodSync(configPath, 384);
618
+ } catch (error) {
619
+ if (descriptor !== void 0) import_node_fs.default.closeSync(descriptor);
620
+ try {
621
+ import_node_fs.default.unlinkSync(tempPath);
622
+ } catch {
623
+ }
624
+ throw error;
625
+ }
589
626
  }
590
627
  function resolveClientOptions(options, config) {
591
628
  return {
@@ -1019,41 +1056,89 @@ function createSavedApiKeyConfig(options, config, apiKey) {
1019
1056
  token: void 0
1020
1057
  };
1021
1058
  }
1059
+ function renderDeviceAuthorizationPrompt(input) {
1060
+ const codeBorder = "\u2500".repeat(input.userCode.length + 4);
1061
+ return `Authorize this device at:
1062
+ ${input.verificationUri}
1063
+
1064
+ \u250C${codeBorder}\u2510
1065
+ \u2502 ${input.userCode} \u2502
1066
+ \u2514${codeBorder}\u2518
1067
+ `;
1068
+ }
1069
+ function getTransientDevicePollDelay(intervalMs, transientBackoffMs) {
1070
+ return Math.min(3e4, Math.max(intervalMs, transientBackoffMs));
1071
+ }
1072
+ function advanceTransientDevicePollBackoff(intervalMs, transientBackoffMs) {
1073
+ return {
1074
+ delayMs: getTransientDevicePollDelay(intervalMs, transientBackoffMs),
1075
+ nextBackoffMs: Math.min(3e4, transientBackoffMs * 2)
1076
+ };
1077
+ }
1022
1078
  async function handleAuthLogin(client, options, commandArgs, config) {
1079
+ assertAuthLoginArgs(commandArgs);
1023
1080
  const noOpen = hasBooleanFlag(commandArgs, "--no-open");
1024
- const start = await client.auth.loginStart({
1025
- baseUrl: options.baseUrl ?? config.baseUrl
1026
- });
1081
+ const noQr = hasBooleanFlag(commandArgs, "--no-qr");
1082
+ const deviceName = parseOptionalFlagValue(commandArgs, "--device-name") ?? `${import_node_os.default.hostname()} r5dctl`;
1083
+ const workerLabel = parseOptionalFlagValue(commandArgs, "--worker-label");
1084
+ const start = await client.auth.deviceStart({ deviceName, workerLabel });
1027
1085
  if (options.json) {
1028
1086
  writeDataOutput(
1029
1087
  true,
1030
1088
  {
1031
1089
  status: "pending",
1032
1090
  requestId: start.requestId,
1033
- loginUrl: start.loginUrl,
1091
+ userCode: start.userCode,
1092
+ verificationUri: start.verificationUri,
1093
+ verificationUriComplete: start.verificationUriComplete,
1034
1094
  expiresAt: start.expiresAt,
1035
1095
  intervalMs: start.intervalMs
1036
1096
  },
1037
1097
  ""
1038
1098
  );
1039
1099
  } else {
1040
- process.stdout.write(`Open this URL to approve login:
1041
- ${start.loginUrl}
1100
+ process.stdout.write(renderDeviceAuthorizationPrompt(start));
1101
+ if (!noQr) {
1102
+ process.stdout.write(`
1103
+ ${await import_qrcode.default.toString(start.verificationUriComplete, { type: "terminal", small: true })}
1042
1104
  `);
1105
+ }
1043
1106
  }
1044
1107
  if (!noOpen) {
1045
1108
  try {
1046
- await openInBrowser(start.loginUrl);
1109
+ await openInBrowser(start.verificationUriComplete);
1047
1110
  } catch {
1048
1111
  }
1049
1112
  }
1050
1113
  const expiresAtMs = new Date(start.expiresAt).getTime();
1051
1114
  let intervalMs = start.intervalMs;
1115
+ let nextDelayMs = intervalMs;
1116
+ let transientBackoffMs = 1e3;
1052
1117
  while (Date.now() < expiresAtMs) {
1053
- await (0, import_promises.setTimeout)(intervalMs);
1054
- const polled = await client.auth.loginPoll(start.requestId, { pollCode: start.pollCode });
1118
+ await (0, import_promises.setTimeout)(Math.min(nextDelayMs, Math.max(0, expiresAtMs - Date.now())));
1119
+ let polled;
1120
+ try {
1121
+ polled = await client.auth.devicePoll(start.requestId, { deviceCode: start.deviceCode });
1122
+ } catch (error) {
1123
+ if (error instanceof import_r5d_api.R5dctlApiError && error.status === 429) {
1124
+ const body = error.body;
1125
+ if (typeof body?.intervalMs === "number") intervalMs = body.intervalMs;
1126
+ nextDelayMs = intervalMs;
1127
+ continue;
1128
+ }
1129
+ const transient = !(error instanceof import_r5d_api.R5dctlApiError) || error.status === 408 || error.status >= 500;
1130
+ if (transient) {
1131
+ const retry = advanceTransientDevicePollBackoff(intervalMs, transientBackoffMs);
1132
+ nextDelayMs = retry.delayMs;
1133
+ transientBackoffMs = retry.nextBackoffMs;
1134
+ continue;
1135
+ }
1136
+ throw error;
1137
+ }
1138
+ transientBackoffMs = 1e3;
1055
1139
  if (polled.status === "pending") {
1056
1140
  intervalMs = polled.intervalMs;
1141
+ nextDelayMs = intervalMs;
1057
1142
  continue;
1058
1143
  }
1059
1144
  if (polled.status === "approved") {
@@ -1078,6 +1163,9 @@ ${start.loginUrl}
1078
1163
  if (polled.status === "expired") {
1079
1164
  throw new Error("Login request expired. Run `r5dctl auth login` again.");
1080
1165
  }
1166
+ if (polled.status === "denied") {
1167
+ throw new Error("Device authorization denied.");
1168
+ }
1081
1169
  if (polled.status === "consumed") {
1082
1170
  throw new Error("Login request was already consumed.");
1083
1171
  }
@@ -2584,6 +2672,7 @@ async function main(argv = process.argv.slice(2)) {
2584
2672
  }
2585
2673
  // Annotate the CommonJS export names for ESM import in node:
2586
2674
  0 && (module.exports = {
2675
+ advanceTransientDevicePollBackoff,
2587
2676
  collectK8sUsage,
2588
2677
  formatK8sCpu,
2589
2678
  formatK8sMemory,
@@ -2592,6 +2681,8 @@ async function main(argv = process.argv.slice(2)) {
2592
2681
  getK8sUsageState,
2593
2682
  getProjectEnvValue,
2594
2683
  getR5dctlVersion,
2684
+ getTransientDevicePollDelay,
2685
+ handleAuthLogin,
2595
2686
  main,
2596
2687
  parseAnswerFlags,
2597
2688
  parseConversationRenderArgs,
@@ -2611,6 +2702,7 @@ async function main(argv = process.argv.slice(2)) {
2611
2702
  renderConversationOverviewResponse,
2612
2703
  renderConversationResponse,
2613
2704
  renderConversationWorkResponse,
2705
+ renderDeviceAuthorizationPrompt,
2614
2706
  renderEnvValues,
2615
2707
  renderK8sUsageReport,
2616
2708
  renderProcessHistory,
@@ -2620,5 +2712,6 @@ async function main(argv = process.argv.slice(2)) {
2620
2712
  renderWorkspaceSync,
2621
2713
  resolveCommandExecution,
2622
2714
  runR5dctlCli,
2623
- summarizeEnvData
2715
+ summarizeEnvData,
2716
+ writeConfig
2624
2717
  });
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/r5dctl",
3
- "version": "0.0.51",
3
+ "version": "0.0.54",
4
4
  "type": "commonjs"
5
5
  }
package/dist/mjs/cli.mjs CHANGED
@@ -2,6 +2,8 @@ import fs from "node:fs";
2
2
  import os from "node:os";
3
3
  import path from "node:path";
4
4
  import { spawn } from "node:child_process";
5
+ import { randomUUID } from "node:crypto";
6
+ import QRCode from "qrcode";
5
7
  import { setTimeout as sleep } from "node:timers/promises";
6
8
  import { parse as parseDotenv } from "dotenv";
7
9
  import {
@@ -39,7 +41,7 @@ const CLI_GLOBAL_OPTION_HELP = [
39
41
  const CLI_ONLY_HELP_ENTRIES = [
40
42
  {
41
43
  section: "auth",
42
- usage: "auth login [--no-open]",
44
+ usage: "auth login [--no-open] [--no-qr] [--device-name <name>] [--worker-label <label>]",
43
45
  description: "Start the browser login flow."
44
46
  },
45
47
  {
@@ -51,7 +53,7 @@ const CLI_ONLY_HELP_ENTRIES = [
51
53
  const CLI_ONLY_COMMAND_HELP = [
52
54
  {
53
55
  path: ["auth", "login"],
54
- usage: "auth login [--no-open]"
56
+ usage: "auth login [--no-open] [--no-qr] [--device-name <name>] [--worker-label <label>]"
55
57
  },
56
58
  {
57
59
  path: ["shell"],
@@ -383,6 +385,21 @@ function parseOptionalFlagValue(args, flag, shortFlag) {
383
385
  function hasBooleanFlag(args, flag) {
384
386
  return args.includes(flag);
385
387
  }
388
+ function assertAuthLoginArgs(args) {
389
+ const valueFlags = /* @__PURE__ */ new Set(["--device-name", "--worker-label"]);
390
+ const booleanFlags = /* @__PURE__ */ new Set(["--no-open", "--no-qr"]);
391
+ for (let index = 0; index < args.length; index += 1) {
392
+ const arg = args[index];
393
+ if (booleanFlags.has(arg)) continue;
394
+ if (valueFlags.has(arg)) {
395
+ if (!args[index + 1]) throw new Error(`Missing value for ${arg}`);
396
+ index += 1;
397
+ continue;
398
+ }
399
+ if ([...valueFlags].some((flag) => arg.startsWith(`${flag}=`))) continue;
400
+ throw new Error(`Unknown auth login flag: ${arg}`);
401
+ }
402
+ }
386
403
  function parseGlobalArgs(argv) {
387
404
  const options = {
388
405
  json: false,
@@ -514,12 +531,27 @@ function serializeConfig(config) {
514
531
  function writeConfig(configPath, config) {
515
532
  const dir = path.dirname(configPath);
516
533
  fs.mkdirSync(dir, { recursive: true, mode: 448 });
534
+ fs.chmodSync(dir, 448);
517
535
  const serialized = serializeConfig(config);
518
- fs.writeFileSync(configPath, `${JSON.stringify(serialized, null, 2)}
519
- `, {
520
- mode: 384
521
- });
522
- fs.chmodSync(configPath, 384);
536
+ const tempPath = path.join(dir, `.${path.basename(configPath)}.${process.pid}.${randomUUID()}.tmp`);
537
+ let descriptor;
538
+ try {
539
+ descriptor = fs.openSync(tempPath, "wx", 384);
540
+ fs.writeFileSync(descriptor, `${JSON.stringify(serialized, null, 2)}
541
+ `, "utf8");
542
+ fs.fsyncSync(descriptor);
543
+ fs.closeSync(descriptor);
544
+ descriptor = void 0;
545
+ fs.renameSync(tempPath, configPath);
546
+ fs.chmodSync(configPath, 384);
547
+ } catch (error) {
548
+ if (descriptor !== void 0) fs.closeSync(descriptor);
549
+ try {
550
+ fs.unlinkSync(tempPath);
551
+ } catch {
552
+ }
553
+ throw error;
554
+ }
523
555
  }
524
556
  function resolveClientOptions(options, config) {
525
557
  return {
@@ -953,41 +985,89 @@ function createSavedApiKeyConfig(options, config, apiKey) {
953
985
  token: void 0
954
986
  };
955
987
  }
988
+ function renderDeviceAuthorizationPrompt(input) {
989
+ const codeBorder = "\u2500".repeat(input.userCode.length + 4);
990
+ return `Authorize this device at:
991
+ ${input.verificationUri}
992
+
993
+ \u250C${codeBorder}\u2510
994
+ \u2502 ${input.userCode} \u2502
995
+ \u2514${codeBorder}\u2518
996
+ `;
997
+ }
998
+ function getTransientDevicePollDelay(intervalMs, transientBackoffMs) {
999
+ return Math.min(3e4, Math.max(intervalMs, transientBackoffMs));
1000
+ }
1001
+ function advanceTransientDevicePollBackoff(intervalMs, transientBackoffMs) {
1002
+ return {
1003
+ delayMs: getTransientDevicePollDelay(intervalMs, transientBackoffMs),
1004
+ nextBackoffMs: Math.min(3e4, transientBackoffMs * 2)
1005
+ };
1006
+ }
956
1007
  async function handleAuthLogin(client, options, commandArgs, config) {
1008
+ assertAuthLoginArgs(commandArgs);
957
1009
  const noOpen = hasBooleanFlag(commandArgs, "--no-open");
958
- const start = await client.auth.loginStart({
959
- baseUrl: options.baseUrl ?? config.baseUrl
960
- });
1010
+ const noQr = hasBooleanFlag(commandArgs, "--no-qr");
1011
+ const deviceName = parseOptionalFlagValue(commandArgs, "--device-name") ?? `${os.hostname()} r5dctl`;
1012
+ const workerLabel = parseOptionalFlagValue(commandArgs, "--worker-label");
1013
+ const start = await client.auth.deviceStart({ deviceName, workerLabel });
961
1014
  if (options.json) {
962
1015
  writeDataOutput(
963
1016
  true,
964
1017
  {
965
1018
  status: "pending",
966
1019
  requestId: start.requestId,
967
- loginUrl: start.loginUrl,
1020
+ userCode: start.userCode,
1021
+ verificationUri: start.verificationUri,
1022
+ verificationUriComplete: start.verificationUriComplete,
968
1023
  expiresAt: start.expiresAt,
969
1024
  intervalMs: start.intervalMs
970
1025
  },
971
1026
  ""
972
1027
  );
973
1028
  } else {
974
- process.stdout.write(`Open this URL to approve login:
975
- ${start.loginUrl}
1029
+ process.stdout.write(renderDeviceAuthorizationPrompt(start));
1030
+ if (!noQr) {
1031
+ process.stdout.write(`
1032
+ ${await QRCode.toString(start.verificationUriComplete, { type: "terminal", small: true })}
976
1033
  `);
1034
+ }
977
1035
  }
978
1036
  if (!noOpen) {
979
1037
  try {
980
- await openInBrowser(start.loginUrl);
1038
+ await openInBrowser(start.verificationUriComplete);
981
1039
  } catch {
982
1040
  }
983
1041
  }
984
1042
  const expiresAtMs = new Date(start.expiresAt).getTime();
985
1043
  let intervalMs = start.intervalMs;
1044
+ let nextDelayMs = intervalMs;
1045
+ let transientBackoffMs = 1e3;
986
1046
  while (Date.now() < expiresAtMs) {
987
- await sleep(intervalMs);
988
- const polled = await client.auth.loginPoll(start.requestId, { pollCode: start.pollCode });
1047
+ await sleep(Math.min(nextDelayMs, Math.max(0, expiresAtMs - Date.now())));
1048
+ let polled;
1049
+ try {
1050
+ polled = await client.auth.devicePoll(start.requestId, { deviceCode: start.deviceCode });
1051
+ } catch (error) {
1052
+ if (error instanceof R5dctlApiError && error.status === 429) {
1053
+ const body = error.body;
1054
+ if (typeof body?.intervalMs === "number") intervalMs = body.intervalMs;
1055
+ nextDelayMs = intervalMs;
1056
+ continue;
1057
+ }
1058
+ const transient = !(error instanceof R5dctlApiError) || error.status === 408 || error.status >= 500;
1059
+ if (transient) {
1060
+ const retry = advanceTransientDevicePollBackoff(intervalMs, transientBackoffMs);
1061
+ nextDelayMs = retry.delayMs;
1062
+ transientBackoffMs = retry.nextBackoffMs;
1063
+ continue;
1064
+ }
1065
+ throw error;
1066
+ }
1067
+ transientBackoffMs = 1e3;
989
1068
  if (polled.status === "pending") {
990
1069
  intervalMs = polled.intervalMs;
1070
+ nextDelayMs = intervalMs;
991
1071
  continue;
992
1072
  }
993
1073
  if (polled.status === "approved") {
@@ -1012,6 +1092,9 @@ ${start.loginUrl}
1012
1092
  if (polled.status === "expired") {
1013
1093
  throw new Error("Login request expired. Run `r5dctl auth login` again.");
1014
1094
  }
1095
+ if (polled.status === "denied") {
1096
+ throw new Error("Device authorization denied.");
1097
+ }
1015
1098
  if (polled.status === "consumed") {
1016
1099
  throw new Error("Login request was already consumed.");
1017
1100
  }
@@ -2517,6 +2600,7 @@ async function main(argv = process.argv.slice(2)) {
2517
2600
  }
2518
2601
  }
2519
2602
  export {
2603
+ advanceTransientDevicePollBackoff,
2520
2604
  collectK8sUsage,
2521
2605
  formatK8sCpu,
2522
2606
  formatK8sMemory,
@@ -2525,6 +2609,8 @@ export {
2525
2609
  getK8sUsageState,
2526
2610
  getProjectEnvValue,
2527
2611
  getR5dctlVersion,
2612
+ getTransientDevicePollDelay,
2613
+ handleAuthLogin,
2528
2614
  main,
2529
2615
  parseAnswerFlags,
2530
2616
  parseConversationRenderArgs,
@@ -2544,6 +2630,7 @@ export {
2544
2630
  renderConversationOverviewResponse,
2545
2631
  renderConversationResponse,
2546
2632
  renderConversationWorkResponse,
2633
+ renderDeviceAuthorizationPrompt,
2547
2634
  renderEnvValues,
2548
2635
  renderK8sUsageReport,
2549
2636
  renderProcessHistory,
@@ -2553,5 +2640,6 @@ export {
2553
2640
  renderWorkspaceSync,
2554
2641
  resolveCommandExecution,
2555
2642
  runR5dctlCli,
2556
- summarizeEnvData
2643
+ summarizeEnvData,
2644
+ writeConfig
2557
2645
  };
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/r5dctl",
3
- "version": "0.0.51",
3
+ "version": "0.0.54",
4
4
  "type": "module"
5
5
  }
@@ -40,6 +40,7 @@ export declare function parseGlobalArgs(argv: string[]): {
40
40
  options: GlobalOptions;
41
41
  rest: string[];
42
42
  };
43
+ export declare function writeConfig(configPath: string, config: R5dctlConfig): void;
43
44
  export declare function parseAnswerFlags(args: string[]): string[];
44
45
  export type SetEnvOptions = {
45
46
  assignments: string[];
@@ -72,6 +73,16 @@ export type K8sUsageOptions = {
72
73
  window: "24h" | "7d";
73
74
  };
74
75
  export declare function parseK8sUsageArgs(args: string[]): K8sUsageOptions;
76
+ export declare function renderDeviceAuthorizationPrompt(input: {
77
+ verificationUri: string;
78
+ userCode: string;
79
+ }): string;
80
+ export declare function getTransientDevicePollDelay(intervalMs: number, transientBackoffMs: number): number;
81
+ export declare function advanceTransientDevicePollBackoff(intervalMs: number, transientBackoffMs: number): {
82
+ delayMs: number;
83
+ nextBackoffMs: number;
84
+ };
85
+ export declare function handleAuthLogin(client: R5dctlClient, options: GlobalOptions, commandArgs: string[], config: R5dctlConfig): Promise<void>;
75
86
  export declare function renderWorkspaceStatus(status: R5dctlWorkspaceStatus): string;
76
87
  export declare function renderWorkspaceSync(result: R5dctlWorkspaceSyncResult): string;
77
88
  export type K8sUsageSummary = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ricsam/r5dctl",
3
- "version": "0.0.51",
3
+ "version": "0.0.54",
4
4
  "type": "module",
5
5
  "main": "./dist/cjs/cli.cjs",
6
6
  "module": "./dist/mjs/cli.mjs",
@@ -26,8 +26,9 @@
26
26
  "r5dctl": "dist/cjs/main.cjs"
27
27
  },
28
28
  "dependencies": {
29
- "@ricsam/r5d-api": "^0.0.51",
29
+ "@ricsam/r5d-api": "^0.0.54",
30
30
  "dotenv": "^17",
31
+ "qrcode": "^1.5.4",
31
32
  "ws": "^8.18.3"
32
33
  },
33
34
  "files": [