@get-dx/cli 0.3.5 → 0.3.6

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.
@@ -59,14 +59,14 @@ export function authCommand() {
59
59
  throw new CliError("Account web API token or personal access token is required");
60
60
  }
61
61
  }
62
- const runtime = buildRuntime(context, {
62
+ const runtime = await buildRuntime(context, {
63
63
  apiBaseUrl,
64
64
  token,
65
65
  webBaseUrl,
66
66
  });
67
67
  const response = await getAuthInfo(runtime);
68
68
  persistBaseUrls(apiBaseUrl, webBaseUrl);
69
- setToken(apiBaseUrl, token);
69
+ await setToken(apiBaseUrl, token);
70
70
  if (context.json) {
71
71
  renderJson({
72
72
  ...response,
@@ -114,7 +114,7 @@ export function authCommand() {
114
114
  },
115
115
  ]))
116
116
  .action(wrapAction(async (_options, command) => {
117
- const runtime = buildRuntime(getContext(command));
117
+ const runtime = await buildRuntime(getContext(command));
118
118
  const response = await getAuthInfo(runtime);
119
119
  if (runtime.context.json) {
120
120
  renderJson({
@@ -51,7 +51,7 @@ export function entitiesCommand() {
51
51
  if (!options.type) {
52
52
  throw new CliError("--type is required", EXIT_CODES.ARGUMENT_ERROR);
53
53
  }
54
- const runtime = buildRuntime(getContext(command));
54
+ const runtime = await buildRuntime(getContext(command));
55
55
  const resolvedValues = await resolveValuesForEntityType(runtime, options.type, options.property, options.alias);
56
56
  const response = await createEntity(runtime, options.identifier, {
57
57
  type: options.type,
@@ -87,7 +87,7 @@ export function entitiesCommand() {
87
87
  },
88
88
  ]))
89
89
  .action(wrapAction(async (identifier, _options, command) => {
90
- const runtime = buildRuntime(getContext(command));
90
+ const runtime = await buildRuntime(getContext(command));
91
91
  const response = await deleteEntity(runtime, identifier);
92
92
  if (runtime.context.json) {
93
93
  renderJson(response);
@@ -125,7 +125,7 @@ export function entitiesCommand() {
125
125
  },
126
126
  ]))
127
127
  .action(wrapAction(async (identifier, options, command) => {
128
- const runtime = buildRuntime(getContext(command));
128
+ const runtime = await buildRuntime(getContext(command));
129
129
  const resolvedValues = await resolveValuesForExistingEntity(runtime, identifier, options.property, options.alias);
130
130
  const response = await updateEntity(runtime, identifier, {
131
131
  ...getEntityMutationOptionValues(options),
@@ -174,7 +174,7 @@ export function entitiesCommand() {
174
174
  if (!options.identifier) {
175
175
  throw new CliError("--identifier is required", EXIT_CODES.ARGUMENT_ERROR);
176
176
  }
177
- const runtime = buildRuntime(getContext(command));
177
+ const runtime = await buildRuntime(getContext(command));
178
178
  const resolvedValues = await resolveValuesForEntityType(runtime, options.type, options.property, options.alias);
179
179
  const response = await upsertEntity(runtime, options.identifier, {
180
180
  type: options.type,
@@ -210,7 +210,7 @@ export function entitiesCommand() {
210
210
  },
211
211
  ]))
212
212
  .action(wrapAction(async (identifier, options, command) => {
213
- const runtime = buildRuntime(getContext(command));
213
+ const runtime = await buildRuntime(getContext(command));
214
214
  const response = await getEntity(runtime, identifier);
215
215
  const processedResponse = processIncludes(response, options);
216
216
  if (runtime.context.json) {
@@ -247,7 +247,7 @@ export function entitiesCommand() {
247
247
  },
248
248
  ]))
249
249
  .action(wrapAction(async (options, command) => {
250
- const runtime = buildRuntime(getContext(command));
250
+ const runtime = await buildRuntime(getContext(command));
251
251
  const response = await listEntities(runtime, {
252
252
  cursor: options.cursor,
253
253
  limit: options.limit,
@@ -284,7 +284,7 @@ export function entitiesCommand() {
284
284
  },
285
285
  ]))
286
286
  .action(wrapAction(async (identifier, options, command) => {
287
- const runtime = buildRuntime(getContext(command));
287
+ const runtime = await buildRuntime(getContext(command));
288
288
  const response = await getEntityTasks(runtime, identifier, {
289
289
  cursor: options.cursor,
290
290
  limit: options.limit,
@@ -317,7 +317,7 @@ export function entitiesCommand() {
317
317
  },
318
318
  ]))
319
319
  .action(wrapAction(async (identifier, options, command) => {
320
- const runtime = buildRuntime(getContext(command));
320
+ const runtime = await buildRuntime(getContext(command));
321
321
  const response = await getEntityScorecards(runtime, identifier, {
322
322
  cursor: options.cursor,
323
323
  limit: options.limit,
@@ -39,7 +39,7 @@ export function entityTypesCommand() {
39
39
  if (modeCount > 1) {
40
40
  throw new CliError("--from-file and --from-stdin are mutually exclusive", EXIT_CODES.ARGUMENT_ERROR);
41
41
  }
42
- const runtime = buildRuntime(getContext(command));
42
+ const runtime = await buildRuntime(getContext(command));
43
43
  let raw;
44
44
  if (options.fromFile) {
45
45
  raw = readYamlFile(options.fromFile);
@@ -71,7 +71,7 @@ export function entityTypesCommand() {
71
71
  },
72
72
  ]))
73
73
  .action(wrapAction(async (identifier, _options, command) => {
74
- const runtime = buildRuntime(getContext(command));
74
+ const runtime = await buildRuntime(getContext(command));
75
75
  const response = await deleteEntityType(runtime, identifier);
76
76
  if (runtime.context.json) {
77
77
  renderJson(response);
@@ -99,7 +99,7 @@ export function entityTypesCommand() {
99
99
  },
100
100
  ]))
101
101
  .action(wrapAction(async (identifier, options, command) => {
102
- const runtime = buildRuntime(getContext(command));
102
+ const runtime = await buildRuntime(getContext(command));
103
103
  const response = await getEntityType(runtime, identifier);
104
104
  const processedEntityType = processEntityTypeIncludes(response.entity_type, options);
105
105
  if (runtime.context.json) {
@@ -125,7 +125,7 @@ export function entityTypesCommand() {
125
125
  },
126
126
  ]))
127
127
  .action(wrapAction(async (path, options, command) => {
128
- const runtime = buildRuntime(getContext(command));
128
+ const runtime = await buildRuntime(getContext(command));
129
129
  if (options.identifier) {
130
130
  const identifier = options.identifier;
131
131
  let entityTypeResponse;
@@ -190,7 +190,7 @@ export function entityTypesCommand() {
190
190
  },
191
191
  ]))
192
192
  .action(wrapAction(async (options, command) => {
193
- const runtime = buildRuntime(getContext(command));
193
+ const runtime = await buildRuntime(getContext(command));
194
194
  const response = await listEntityTypes(runtime, {
195
195
  cursor: options.cursor,
196
196
  limit: options.limit,
@@ -231,7 +231,7 @@ export function entityTypesCommand() {
231
231
  if (modeCount > 1) {
232
232
  throw new CliError("--from-file and --from-stdin are mutually exclusive", EXIT_CODES.ARGUMENT_ERROR);
233
233
  }
234
- const runtime = buildRuntime(getContext(command));
234
+ const runtime = await buildRuntime(getContext(command));
235
235
  let raw;
236
236
  if (options.fromFile) {
237
237
  raw = readYamlFile(options.fromFile);
@@ -31,7 +31,7 @@ export function initCommand() {
31
31
  ]))
32
32
  .action(wrapAction(async (commandOptions, command) => {
33
33
  const context = getContext(command);
34
- let runtime = buildRuntimeSafe(context);
34
+ let runtime = await buildRuntimeSafe(context);
35
35
  ensureInteractive();
36
36
  await renderWelcomeAnimation();
37
37
  runtime = await ensureLoggedIn(context, runtime);
@@ -92,7 +92,7 @@ async function attemptLogin(apiBaseUrl, webBaseUrl, context) {
92
92
  if (!token) {
93
93
  throw new CliError("Account web API token is required");
94
94
  }
95
- const runtime = buildRuntime(context, {
95
+ const runtime = await buildRuntime(context, {
96
96
  apiBaseUrl,
97
97
  token,
98
98
  webBaseUrl,
@@ -103,7 +103,7 @@ async function attemptLogin(apiBaseUrl, webBaseUrl, context) {
103
103
  throw new CliError(`Login failed`);
104
104
  }
105
105
  persistBaseUrls(apiBaseUrl, webBaseUrl);
106
- setToken(apiBaseUrl, token);
106
+ await setToken(apiBaseUrl, token);
107
107
  renderAuthInfo(response, token, apiBaseUrl);
108
108
  return runtime;
109
109
  }
@@ -39,7 +39,7 @@ export function scorecardsCommand() {
39
39
  if (modeCount > 1) {
40
40
  throw new CliError("--from-file and --from-stdin are mutually exclusive", EXIT_CODES.ARGUMENT_ERROR);
41
41
  }
42
- const runtime = buildRuntime(getContext(command));
42
+ const runtime = await buildRuntime(getContext(command));
43
43
  let raw;
44
44
  if (options.fromFile) {
45
45
  raw = readYamlFile(options.fromFile);
@@ -71,7 +71,7 @@ export function scorecardsCommand() {
71
71
  },
72
72
  ]))
73
73
  .action(wrapAction(async (id, _options, command) => {
74
- const runtime = buildRuntime(getContext(command));
74
+ const runtime = await buildRuntime(getContext(command));
75
75
  await deleteScorecard(runtime, id);
76
76
  if (runtime.context.json) {
77
77
  renderJson({ ok: true });
@@ -103,7 +103,7 @@ export function scorecardsCommand() {
103
103
  ]))
104
104
  .action(wrapAction(async (id, options, command) => {
105
105
  const includeSections = parseScorecardIncludeSections(options.include);
106
- const runtime = buildRuntime(getContext(command));
106
+ const runtime = await buildRuntime(getContext(command));
107
107
  const response = await getScorecard(runtime, id);
108
108
  if (runtime.context.json) {
109
109
  const processedScorecard = processScorecardIncludes(response.scorecard, includeSections);
@@ -129,7 +129,7 @@ export function scorecardsCommand() {
129
129
  },
130
130
  ]))
131
131
  .action(wrapAction(async (path, options, command) => {
132
- const runtime = buildRuntime(getContext(command));
132
+ const runtime = await buildRuntime(getContext(command));
133
133
  if (options.id) {
134
134
  const id = options.id;
135
135
  let scorecardResponse;
@@ -200,7 +200,7 @@ export function scorecardsCommand() {
200
200
  ]))
201
201
  .action(wrapAction(async (options, command) => {
202
202
  const includeSections = parseScorecardIncludeSections(options.include);
203
- const runtime = buildRuntime(getContext(command));
203
+ const runtime = await buildRuntime(getContext(command));
204
204
  const response = await listScorecards(runtime, {
205
205
  cursor: options.cursor,
206
206
  limit: options.limit,
@@ -242,7 +242,7 @@ export function scorecardsCommand() {
242
242
  if (modeCount > 1) {
243
243
  throw new CliError("--from-file and --from-stdin are mutually exclusive", EXIT_CODES.ARGUMENT_ERROR);
244
244
  }
245
- const runtime = buildRuntime(getContext(command));
245
+ const runtime = await buildRuntime(getContext(command));
246
246
  let raw;
247
247
  if (options.fromFile) {
248
248
  raw = readYamlFile(options.fromFile);
@@ -34,7 +34,7 @@ export function snapshotsCommand() {
34
34
  ]))
35
35
  .action(wrapAction(async (options, command) => {
36
36
  const id = parseRequiredTextOption(options.id, "--id");
37
- const runtime = buildRuntime(getContext(command));
37
+ const runtime = await buildRuntime(getContext(command));
38
38
  const response = await listSnapshotCsatComments(runtime, {
39
39
  id,
40
40
  cursor: options.cursor,
@@ -73,7 +73,7 @@ export function snapshotsCommand() {
73
73
  ]))
74
74
  .action(wrapAction(async (options, command) => {
75
75
  const id = parseRequiredTextOption(options.id, "--id");
76
- const runtime = buildRuntime(getContext(command));
76
+ const runtime = await buildRuntime(getContext(command));
77
77
  const response = await listSnapshotDriverComments(runtime, {
78
78
  id,
79
79
  cursor: options.cursor,
@@ -103,7 +103,7 @@ export function snapshotsCommand() {
103
103
  ]))
104
104
  .action(wrapAction(async (options, command) => {
105
105
  const snapshotId = parseRequiredTextOption(options.id, "--id");
106
- const runtime = buildRuntime(getContext(command));
106
+ const runtime = await buildRuntime(getContext(command));
107
107
  const response = await getSnapshotInfo(runtime, snapshotId);
108
108
  if (runtime.context.json) {
109
109
  renderJson(response);
@@ -126,7 +126,7 @@ export function snapshotsCommand() {
126
126
  },
127
127
  ]))
128
128
  .action(wrapAction(async (_options, command) => {
129
- const runtime = buildRuntime(getContext(command));
129
+ const runtime = await buildRuntime(getContext(command));
130
130
  const response = await listSnapshots(runtime);
131
131
  if (runtime.context.json) {
132
132
  renderJson(response);
@@ -50,7 +50,7 @@ export function queryCommand() {
50
50
  throw new CliError("--output cannot be used with --json", EXIT_CODES.ARGUMENT_ERROR);
51
51
  }
52
52
  const variables = parseVariables(options.variable);
53
- const runtime = buildRuntime(context);
53
+ const runtime = await buildRuntime(context);
54
54
  const progress = new AsyncProgressReporter();
55
55
  const queryRun = await executeQuery(runtime, progress, sql, variables);
56
56
  await waitForQuery(runtime, progress, queryRun.id);
@@ -23,7 +23,7 @@ export function teamsCommand() {
23
23
  ]))
24
24
  .action(wrapAction(async (options, command) => {
25
25
  const teamEmails = parseTeamEmails(options.teamEmails);
26
- const runtime = buildRuntime(getContext(command));
26
+ const runtime = await buildRuntime(getContext(command));
27
27
  const response = await findTeamByMembers(runtime, teamEmails);
28
28
  if (runtime.context.json) {
29
29
  renderJson(response);
@@ -53,7 +53,7 @@ export function teamsCommand() {
53
53
  ]))
54
54
  .action(wrapAction(async (options, command) => {
55
55
  const lookup = parseTeamInfoLookup(options);
56
- const runtime = buildRuntime(getContext(command));
56
+ const runtime = await buildRuntime(getContext(command));
57
57
  const response = await getTeamInfo(runtime, lookup);
58
58
  if (runtime.context.json) {
59
59
  renderJson(response);
@@ -76,7 +76,7 @@ export function teamsCommand() {
76
76
  },
77
77
  ]))
78
78
  .action(wrapAction(async (_options, command) => {
79
- const runtime = buildRuntime(getContext(command));
79
+ const runtime = await buildRuntime(getContext(command));
80
80
  const response = await listTeams(runtime);
81
81
  if (runtime.context.json) {
82
82
  renderJson(response);
@@ -19,7 +19,7 @@ export function addLinkCommand() {
19
19
  ]))
20
20
  .action(wrapAction(async (workflowRunId, options, command) => {
21
21
  const context = getContext(command);
22
- const runtime = buildRuntime(context);
22
+ const runtime = await buildRuntime(context);
23
23
  const response = await addLink(runtime, workflowRunId, options.url, options.label);
24
24
  if (runtime.context.json) {
25
25
  renderJson(response);
@@ -23,7 +23,7 @@ export function changeStatusCommand() {
23
23
  ]))
24
24
  .action(wrapAction(async (workflowRunId, options, command) => {
25
25
  const context = getContext(command);
26
- const runtime = buildRuntime(context);
26
+ const runtime = await buildRuntime(context);
27
27
  if (!isValidStatus(options.status)) {
28
28
  throw new CliError(`Invalid status: ${options.status}. Must be one of: SUCCEEDED, FAILED.`, EXIT_CODES.ARGUMENT_ERROR);
29
29
  }
@@ -18,7 +18,7 @@ export function infoCommand() {
18
18
  ]))
19
19
  .action(wrapAction(async (workflowRunId, options, command) => {
20
20
  const context = getContext(command);
21
- const runtime = buildRuntime(context);
21
+ const runtime = await buildRuntime(context);
22
22
  const infoResponse = await getWorkflowRun(runtime, workflowRunId);
23
23
  const workflowRun = infoResponse.body.workflow_run;
24
24
  if (runtime.context.json) {
@@ -22,7 +22,7 @@ export function postMessageCommand() {
22
22
  ]))
23
23
  .action(wrapAction(async (workflowRunId, options, command) => {
24
24
  const context = getContext(command);
25
- const runtime = buildRuntime(context);
25
+ const runtime = await buildRuntime(context);
26
26
  const response = await postMessage(runtime, workflowRunId, options.message);
27
27
  if (runtime.context.json) {
28
28
  renderJson(response);
@@ -52,7 +52,7 @@ export function triggerCommand() {
52
52
  ]))
53
53
  .action(wrapAction(async (workflowIdentifier, options, command) => {
54
54
  const context = getContext(command);
55
- const runtime = buildRuntime(context);
55
+ const runtime = await buildRuntime(context);
56
56
  // Fetch list of all workflows, find the one that applies
57
57
  const workflowsResponse = await listWorkflows(runtime, {});
58
58
  const workflow = workflowsResponse.workflows.find((w) => w.identifier === workflowIdentifier);
@@ -35,7 +35,7 @@ export function workflowsCommand() {
35
35
  if (entityIdentifier && scope !== "ENTITY") {
36
36
  throw new CliError("--entity-identifier requires --scope ENTITY", EXIT_CODES.ARGUMENT_ERROR);
37
37
  }
38
- const runtime = buildRuntime(getContext(command));
38
+ const runtime = await buildRuntime(getContext(command));
39
39
  const response = await listWorkflows(runtime, {
40
40
  scope,
41
41
  entity_identifier: entityIdentifier,
package/dist/runtime.js CHANGED
@@ -6,10 +6,10 @@ import cliPackage from "../package.json" with { type: "json" };
6
6
  export function buildLogger(context) {
7
7
  return createLogger({ json: context.json || !process.stderr.isTTY });
8
8
  }
9
- export function buildRuntime(context, overrides) {
9
+ export async function buildRuntime(context, overrides) {
10
10
  const apiBaseUrl = overrides?.apiBaseUrl ?? resolveApiBaseUrl();
11
11
  const webBaseUrl = overrides?.webBaseUrl ?? resolveWebBaseUrl();
12
- const token = overrides?.token ?? getToken(apiBaseUrl);
12
+ const token = overrides?.token ?? (await getToken(apiBaseUrl));
13
13
  if (!token) {
14
14
  throw new CliError("No API token configured. Run `dx auth login --token <token>` or set DX_API_TOKEN.");
15
15
  }
@@ -22,10 +22,10 @@ export function buildRuntime(context, overrides) {
22
22
  logger: buildLogger(context),
23
23
  };
24
24
  }
25
- export function buildRuntimeSafe(context, overrides) {
25
+ export async function buildRuntimeSafe(context, overrides) {
26
26
  const apiBaseUrl = overrides?.apiBaseUrl ?? resolveApiBaseUrl();
27
27
  const webBaseUrl = overrides?.webBaseUrl ?? resolveWebBaseUrl();
28
- const token = overrides?.token ?? getToken(apiBaseUrl);
28
+ const token = overrides?.token ?? (await getToken(apiBaseUrl));
29
29
  if (!token) {
30
30
  return null;
31
31
  }
package/dist/secrets.js CHANGED
@@ -1,17 +1,18 @@
1
- import { execaSync } from "execa";
1
+ import { execa } from "execa";
2
+ import * as crossKeychain from "cross-keychain";
2
3
  import { CliError } from "./errors.js";
3
4
  const SERVICE = "dx-cli";
4
- export function getToken(baseUrl) {
5
+ export async function getToken(baseUrl) {
5
6
  if (process.env.DX_API_TOKEN) {
6
7
  return process.env.DX_API_TOKEN;
7
8
  }
8
- return getSecretStore().get(baseUrl);
9
+ return await getSecretStore().get(baseUrl);
9
10
  }
10
- export function setToken(baseUrl, token) {
11
- getSecretStore().set(baseUrl, token);
11
+ export async function setToken(baseUrl, token) {
12
+ await getSecretStore().set(baseUrl, token);
12
13
  }
13
- export function deleteToken(baseUrl) {
14
- getSecretStore().delete(baseUrl);
14
+ export async function deleteToken(baseUrl) {
15
+ await getSecretStore().delete(baseUrl);
15
16
  }
16
17
  function getSecretStore(platform = process.platform) {
17
18
  if (platform === "darwin") {
@@ -20,28 +21,32 @@ function getSecretStore(platform = process.platform) {
20
21
  if (platform === "linux") {
21
22
  return linuxSecretStore();
22
23
  }
24
+ if (platform === "win32") {
25
+ return windowsSecretStore();
26
+ }
23
27
  throw new CliError(`Unsupported platform for secure token storage: ${platform}`);
24
28
  }
25
29
  function macosSecretStore() {
26
30
  return {
27
- get(baseUrl) {
31
+ async get(baseUrl) {
28
32
  try {
29
- return execaSync("security", [
33
+ const result = await execa("security", [
30
34
  "find-generic-password",
31
35
  "-s",
32
36
  SERVICE,
33
37
  "-a",
34
38
  baseUrl,
35
39
  "-w",
36
- ]).stdout.trim();
40
+ ]);
41
+ return result.stdout?.trim() ?? null;
37
42
  }
38
43
  catch {
39
44
  return null;
40
45
  }
41
46
  },
42
- set(baseUrl, token) {
47
+ async set(baseUrl, token) {
43
48
  try {
44
- execaSync("security", [
49
+ await execa("security", [
45
50
  "add-generic-password",
46
51
  "-U",
47
52
  "-s",
@@ -63,9 +68,9 @@ function macosSecretStore() {
63
68
  .join("\n"));
64
69
  }
65
70
  },
66
- delete(baseUrl) {
71
+ async delete(baseUrl) {
67
72
  try {
68
- execaSync("security", [
73
+ await execa("security", [
69
74
  "delete-generic-password",
70
75
  "-s",
71
76
  SERVICE,
@@ -81,33 +86,70 @@ function macosSecretStore() {
81
86
  }
82
87
  function linuxSecretStore() {
83
88
  return {
84
- get(baseUrl) {
89
+ async get(baseUrl) {
85
90
  try {
86
- return execaSync("secret-tool", [
91
+ const result = await execa("secret-tool", [
87
92
  "lookup",
88
93
  "service",
89
94
  SERVICE,
90
95
  "account",
91
96
  baseUrl,
92
- ]).stdout.trim();
97
+ ]);
98
+ return result.stdout?.trim() ?? null;
93
99
  }
94
100
  catch {
95
101
  return null;
96
102
  }
97
103
  },
98
- set(baseUrl, token) {
104
+ async set(baseUrl, token) {
99
105
  try {
100
- execaSync("secret-tool", ["store", "--label=dx-cli", "service", SERVICE, "account", baseUrl], { input: token });
106
+ await execa("secret-tool", ["store", "--label=dx-cli", "service", SERVICE, "account", baseUrl], { input: token });
101
107
  }
102
108
  catch (error) {
103
109
  throw buildLinuxTokenStorageError(error);
104
110
  }
105
111
  },
106
- delete(_baseUrl) {
112
+ async delete(_baseUrl) {
107
113
  throw new CliError("Linux token deletion is not supported by secret-tool in this implementation");
108
114
  },
109
115
  };
110
116
  }
117
+ function windowsSecretStore() {
118
+ return {
119
+ async get(baseUrl) {
120
+ try {
121
+ return await crossKeychain.getPassword(SERVICE, encodeAccountName(baseUrl));
122
+ }
123
+ catch {
124
+ return null;
125
+ }
126
+ },
127
+ async set(baseUrl, token) {
128
+ try {
129
+ await crossKeychain.setPassword(SERVICE, encodeAccountName(baseUrl), token);
130
+ }
131
+ catch (error) {
132
+ throw new CliError([
133
+ "Unable to save the DX API token securely using Windows Credential Manager.",
134
+ "",
135
+ "Set DX_API_TOKEN in the environment, or try again from a Windows session with Credential Manager access.",
136
+ formatErrorDetails(error),
137
+ ]
138
+ .filter(Boolean)
139
+ .join("\n"));
140
+ }
141
+ },
142
+ async delete(baseUrl) {
143
+ await crossKeychain.deletePassword(SERVICE, encodeAccountName(baseUrl));
144
+ },
145
+ };
146
+ }
147
+ /**
148
+ * Encode an account name so it has only valid characters for Windows.
149
+ */
150
+ function encodeAccountName(baseUrl) {
151
+ return Buffer.from(baseUrl).toString("base64");
152
+ }
111
153
  function buildLinuxTokenStorageError(error) {
112
154
  if (hasErrorCode(error, "ENOENT")) {
113
155
  return new CliError([
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@get-dx/cli",
3
- "version": "0.3.5",
3
+ "version": "0.3.6",
4
4
  "description": "DX CLI",
5
5
  "author": "DX <developers@getdx.com>",
6
6
  "bugs": {
@@ -41,6 +41,7 @@
41
41
  "dependencies": {
42
42
  "@inquirer/prompts": "^8.4.1",
43
43
  "commander": "^14.0.1",
44
+ "cross-keychain": "^1.1.0",
44
45
  "dayjs": "^1.11.20",
45
46
  "execa": "^9.6.1",
46
47
  "open": "^11.0.0",