@rankwrangler/cli 0.5.0 → 0.8.0

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,68 +1,24 @@
1
1
  # @rankwrangler/cli
2
2
 
3
- Official CLI for RankWrangler.
4
-
5
- ## Install
3
+ Official agent-oriented CLI for RankWrangler.
6
4
 
7
5
  ```bash
8
6
  npm install -g @rankwrangler/cli
9
- ```
10
-
11
- ## Usage
12
-
13
- ```bash
14
- rw --version
15
- rw changelog
16
- rw auth set rrk_...
17
- rw config set storage-dir ~/.config/rankwrangler
18
- rw products get B0DV53VS61
19
- rw products history B0DV53VS61 --metrics bsr,price
20
- RR_LICENSE_KEY=rrk_... rw license status
21
- # `rankwrangler` is also supported as an alias
22
- ```
23
-
24
- `rw auth set <licenseKey>` stores the license key in the platform secure store
25
- (`macOS Keychain` on macOS). `rw auth status` reports whether the CLI will use an env override,
26
- stored auth, or no auth. `rw auth clear` removes the stored key.
27
-
28
- `rw config set storage-dir <path>` saves the active storage directory globally. After that, every
29
- CLI command reads and writes its non-secret config/data from that directory, while preserving
30
- existing config values when switching. `RR_LICENSE_KEY`, `RR_STORAGE_DIR`, `RR_API_URL`, and
31
- `RR_MARKETPLACE_ID` override saved CLI state when set for CI, automation, or agent runtimes.
32
- `rw --version` prints the installed CLI version. `rw changelog` prints the latest bundled release
33
- notes shipped with the package.
34
-
35
- ## Development
36
7
 
37
- ```bash
38
- bun run cli:build
39
- bun run cli:test:e2e
8
+ rw auth set ak_...
9
+ rw product get B0DV53VS61
10
+ rw product search "retro gardening shirt"
11
+ rw product history B0DV53VS61 --bucket week
12
+ rw keyword get "retro gardening shirt"
13
+ rw keyword search "retro gardening"
14
+ rw keyword history "retro gardening shirt" --rangeDays 90
40
15
  ```
41
16
 
42
- ## Release (Maintainers)
43
-
44
- From repo root:
45
-
46
- ```bash
47
- bun run release:bump patch
48
- bun install
49
- bun run release:collect-changelog-context
50
- # draft CHANGELOG.md entry from commit context
51
- bun run release:check
52
- bun run cli:build
53
- bun run release:check-cli-pack
54
- ```
17
+ The public command surface is Product `get/search/history`, keyword `get/search/history`, auth,
18
+ config, version, and changelog. Product `get/history` and keyword reads wait for policy-current
19
+ data and expose no refresh control; Product search retains its separate search contract. Search
20
+ data contains `keyword`, `searchedAt`, and compact results with `organicSearchPlacement`. Output is
21
+ a JSON `{ ok, data }` envelope; failures use `{ ok: false, error }` on stderr.
22
+ The CLI does not expose Catalog, Operation, provider status, or polling commands.
55
23
 
56
- Then publish:
57
-
58
- Publish `packages/http-client` first. `packages/cli` now fails fast on `npm publish` until the
59
- matching `@rankwrangler/http-client` version is already available on npm.
60
-
61
- ```bash
62
- cd packages/cli
63
- set -a
64
- source ../../.env
65
- set +a
66
- npm whoami --userconfig ../../.npmrc
67
- npm publish --access public --userconfig ../../.npmrc
68
- ```
24
+ See the [CLI reference](../../docs/reference/cli.md) for options and release workflow.
package/dist/CHANGELOG.md CHANGED
@@ -2,6 +2,60 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## v0.8.0 - 2026-08-09
6
+
7
+ ### Added
8
+
9
+ - Add synchronous basic Product lookup for up to 200 marketplace/ASIN pairs through the public
10
+ API and hosted MCP, with smart single- or multi-ASIN routing in `rw product get`.
11
+ - Add complete Product Search, source provenance, and nullable merch-listing classification to the
12
+ public Product intelligence surface.
13
+
14
+ ### Changed
15
+
16
+ - Resolve public Product reads to policy-current, source-attributed results while batching cold
17
+ SP-API catalog work by marketplace and persisting every requested Product identity.
18
+
19
+ ### Fixed
20
+
21
+ - Preserve unavailable listing data, stop repeatedly polling unavailable listings, and keep those
22
+ Products accessible from the dashboard drawer.
23
+ - Expose the hosted MCP ingress correctly in production.
24
+
25
+ ## v0.7.0 - 2026-08-06
26
+
27
+ ### Added
28
+
29
+ - Add typed Product and keyword `get`, `search`, and `history` contracts for agents and integrations.
30
+ - Add Merchbase OAuth bearer authentication and centralized RankWrangler access metering across
31
+ public clients.
32
+
33
+ ### Changed
34
+
35
+ - Breaking: replace the previous public client surface with the grouped `product.get`,
36
+ `product.search`, `product.history`, `keyword.get`, `keyword.search`, and `keyword.history`
37
+ procedures.
38
+ - Return final source-attributed data and freshness through the public API while keeping Catalog,
39
+ Operation, polling, and provider orchestration internal.
40
+
41
+ ## v0.6.0 - 2026-06-15
42
+
43
+ ### Added
44
+
45
+ - Add `rw auth set --stdin` and hidden local auth entry so agents can pipe license keys and humans
46
+ can avoid shell history.
47
+ - Add `rw config get`, `rw config unset`, and `rw config reset` for explicit non-secret config
48
+ inspection and cleanup.
49
+
50
+ ### Changed
51
+
52
+ - Show auth source/status in `rw config show` without printing secrets.
53
+ - Update CLI help, README, CLI spec, and e2e coverage for the clearer auth/config surface.
54
+
55
+ ### Removed
56
+
57
+ - Remove `rw config clear` in favor of `rw config unset <key>` and `rw config reset`.
58
+
5
59
  ## v0.5.0 - 2026-03-18
6
60
 
7
61
  ### Added
@@ -202,4 +256,5 @@ All notable changes to this project will be documented in this file.
202
256
 
203
257
  - Rename and standardize the typed npm client package to `@merchbase/rankwrangler-http-client`.
204
258
  - Set initial HTTP client package release to `0.1.0` in `packages/http-client/package.json`.
205
- - Document npm publish and versioning policy for the typed HTTP client in `docs/http-client-spec.md`.
259
+ - Document npm publish and versioning policy for the typed HTTP client; the current owner is
260
+ `docs/reference/http-client.md`.
@@ -0,0 +1,5 @@
1
+ export declare class CliAuthError extends Error {
2
+ readonly code: string;
3
+ readonly details?: unknown;
4
+ constructor(code: string, message: string, details?: unknown);
5
+ }
@@ -0,0 +1,10 @@
1
+ export class CliAuthError extends Error {
2
+ code;
3
+ details;
4
+ constructor(code, message, details) {
5
+ super(message);
6
+ this.name = 'CliAuthError';
7
+ this.code = code;
8
+ this.details = details;
9
+ }
10
+ }
@@ -1,18 +1,16 @@
1
- export declare const LICENSE_KEY_ENV_VAR = "RR_LICENSE_KEY";
2
- export declare const AUTH_SERVICE_NAME = "RankWrangler CLI";
3
- export declare const AUTH_ACCOUNT_NAME = "license-key";
1
+ export declare const API_KEY_ENV_VAR = "MERCHBASE_API_KEY";
2
+ export declare const AUTH_SERVICE_NAME = "co.merchbase.cli";
3
+ export declare const AUTH_ACCOUNT_NAME = "api-key";
4
4
  type CliFail = (code: string, message: string, details?: unknown) => never;
5
5
  type AuthCommand = {
6
6
  verb: string;
7
7
  args: string[];
8
8
  };
9
- export declare class CliAuthError extends Error {
10
- readonly code: string;
11
- readonly details?: unknown;
12
- constructor(code: string, message: string, details?: unknown);
13
- }
9
+ type AuthCommandOptions = {
10
+ stdin?: boolean;
11
+ };
14
12
  export declare const resolveApiKey: () => Promise<string | undefined>;
15
- export declare const runAuthCommand: (command: AuthCommand, fail: CliFail) => Promise<{
13
+ export declare const runAuthCommand: (command: AuthCommand, fail: CliFail, options?: AuthCommandOptions) => Promise<{
16
14
  source: string;
17
15
  envOverride: boolean;
18
16
  secureStore: {
@@ -20,7 +18,7 @@ export declare const runAuthCommand: (command: AuthCommand, fail: CliFail) => Pr
20
18
  serviceName: string;
21
19
  accountName: string;
22
20
  available: boolean;
23
- hasStoredLicenseKey: boolean;
21
+ hasStoredApiKey: boolean;
24
22
  error?: undefined;
25
23
  };
26
24
  } | {
@@ -31,7 +29,7 @@ export declare const runAuthCommand: (command: AuthCommand, fail: CliFail) => Pr
31
29
  serviceName: string;
32
30
  accountName: string;
33
31
  available: boolean;
34
- hasStoredLicenseKey: null;
32
+ hasStoredApiKey: null;
35
33
  error: string;
36
34
  };
37
35
  } | {
@@ -43,7 +41,7 @@ export declare const runAuthCommand: (command: AuthCommand, fail: CliFail) => Pr
43
41
  serviceName: string;
44
42
  accountName: string;
45
43
  available: boolean;
46
- hasStoredLicenseKey: boolean;
44
+ hasStoredApiKey: boolean;
47
45
  error?: undefined;
48
46
  };
49
47
  } | {
@@ -55,7 +53,7 @@ export declare const runAuthCommand: (command: AuthCommand, fail: CliFail) => Pr
55
53
  serviceName: string;
56
54
  accountName: string;
57
55
  available: boolean;
58
- hasStoredLicenseKey: null;
56
+ hasStoredApiKey: null;
59
57
  error: string;
60
58
  };
61
59
  } | {
@@ -67,7 +65,7 @@ export declare const runAuthCommand: (command: AuthCommand, fail: CliFail) => Pr
67
65
  serviceName: string;
68
66
  accountName: string;
69
67
  available: boolean;
70
- hasStoredLicenseKey: boolean;
68
+ hasStoredApiKey: boolean;
71
69
  error?: undefined;
72
70
  };
73
71
  } | {
@@ -79,7 +77,30 @@ export declare const runAuthCommand: (command: AuthCommand, fail: CliFail) => Pr
79
77
  serviceName: string;
80
78
  accountName: string;
81
79
  available: boolean;
82
- hasStoredLicenseKey: null;
80
+ hasStoredApiKey: null;
81
+ error: string;
82
+ };
83
+ }>;
84
+ export declare const buildAuthStatus: () => Promise<{
85
+ source: string;
86
+ envOverride: boolean;
87
+ secureStore: {
88
+ backend: string;
89
+ serviceName: string;
90
+ accountName: string;
91
+ available: boolean;
92
+ hasStoredApiKey: boolean;
93
+ error?: undefined;
94
+ };
95
+ } | {
96
+ source: string;
97
+ envOverride: boolean;
98
+ secureStore: {
99
+ backend: string;
100
+ serviceName: string;
101
+ accountName: string;
102
+ available: boolean;
103
+ hasStoredApiKey: null;
83
104
  error: string;
84
105
  };
85
106
  }>;
package/dist/cli-auth.js CHANGED
@@ -1,33 +1,27 @@
1
+ import { spawnSync } from 'node:child_process';
1
2
  import { mkdir, readFile, rm, writeFile } from 'node:fs/promises';
2
3
  import path from 'node:path';
3
- export const LICENSE_KEY_ENV_VAR = 'RR_LICENSE_KEY';
4
- export const AUTH_SERVICE_NAME = 'RankWrangler CLI';
5
- export const AUTH_ACCOUNT_NAME = 'license-key';
4
+ import readline from 'node:readline';
5
+ import { MERCHBASE_API_KEY_ENV, MERCHBASE_API_KEY_KEYCHAIN_ACCOUNT, MERCHBASE_API_KEY_KEYCHAIN_SERVICE, } from '@merchbaseco/access';
6
+ import { CliAuthError } from './cli-auth-error.js';
7
+ export const API_KEY_ENV_VAR = MERCHBASE_API_KEY_ENV;
8
+ export const AUTH_SERVICE_NAME = MERCHBASE_API_KEY_KEYCHAIN_SERVICE;
9
+ export const AUTH_ACCOUNT_NAME = MERCHBASE_API_KEY_KEYCHAIN_ACCOUNT;
6
10
  const INTERNAL_SECRET_STORE_DIR_ENV_VAR = 'RW_INTERNAL_CLI_SECRET_STORE_DIR';
7
- export class CliAuthError extends Error {
8
- code;
9
- details;
10
- constructor(code, message, details) {
11
- super(message);
12
- this.name = 'CliAuthError';
13
- this.code = code;
14
- this.details = details;
15
- }
16
- }
17
11
  export const resolveApiKey = async () => {
18
- const envLicenseKey = resolveEnvLicenseKey();
19
- if (envLicenseKey) {
20
- return envLicenseKey;
12
+ const envApiKey = resolveEnvApiKey();
13
+ if (envApiKey) {
14
+ return envApiKey;
21
15
  }
22
16
  const secureStore = await createSecureStore();
23
17
  return (await secureStore.get()) ?? undefined;
24
18
  };
25
- export const runAuthCommand = async (command, fail) => {
19
+ export const runAuthCommand = async (command, fail, options = {}) => {
26
20
  if (command.verb === 'status') {
27
21
  return buildAuthStatus();
28
22
  }
29
23
  if (command.verb === 'set') {
30
- const value = resolveAuthSetValue(command.args, fail);
24
+ const value = await resolveAuthSetValue(command.args, fail, options);
31
25
  const secureStore = await createSecureStore();
32
26
  await secureStore.set(value);
33
27
  return {
@@ -45,51 +39,100 @@ export const runAuthCommand = async (command, fail) => {
45
39
  }
46
40
  fail('UNKNOWN_COMMAND', 'Unknown auth command', { verb: command.verb });
47
41
  };
48
- const buildAuthStatus = async () => {
49
- const envLicenseKey = resolveEnvLicenseKey();
42
+ export const buildAuthStatus = async () => {
43
+ const envApiKey = resolveEnvApiKey();
50
44
  try {
51
45
  const secureStore = await createSecureStore();
52
- const storedLicenseKey = await secureStore.get();
46
+ const storedApiKey = await secureStore.get();
53
47
  return {
54
- source: envLicenseKey ? 'env' : storedLicenseKey ? 'secure-store' : 'none',
55
- envOverride: Boolean(envLicenseKey),
48
+ source: envApiKey ? 'env' : storedApiKey ? 'secure-store' : 'none',
49
+ envOverride: Boolean(envApiKey),
56
50
  secureStore: {
57
51
  backend: secureStore.backend,
58
52
  serviceName: secureStore.serviceName,
59
53
  accountName: secureStore.accountName,
60
54
  available: true,
61
- hasStoredLicenseKey: Boolean(storedLicenseKey),
55
+ hasStoredApiKey: Boolean(storedApiKey),
62
56
  },
63
57
  };
64
58
  }
65
59
  catch (error) {
66
60
  return {
67
- source: envLicenseKey ? 'env' : 'none',
68
- envOverride: Boolean(envLicenseKey),
61
+ source: envApiKey ? 'env' : 'none',
62
+ envOverride: Boolean(envApiKey),
69
63
  secureStore: {
70
64
  backend: getSecureStoreBackendLabel(),
71
65
  serviceName: AUTH_SERVICE_NAME,
72
66
  accountName: AUTH_ACCOUNT_NAME,
73
67
  available: false,
74
- hasStoredLicenseKey: null,
68
+ hasStoredApiKey: null,
75
69
  error: error instanceof Error ? error.message : 'Unknown secure-store error',
76
70
  },
77
71
  };
78
72
  }
79
73
  };
80
- const resolveAuthSetValue = (args, fail) => {
74
+ const resolveAuthSetValue = async (args, fail, options) => {
81
75
  const positionalValue = args.join(' ').trim();
82
76
  if (positionalValue) {
83
77
  return positionalValue;
84
78
  }
85
- const envLicenseKey = resolveEnvLicenseKey();
86
- if (envLicenseKey) {
87
- return envLicenseKey;
79
+ if (options.stdin) {
80
+ const stdinValue = await readSecretFromStdin();
81
+ if (stdinValue) {
82
+ return stdinValue;
83
+ }
84
+ fail('INVALID_INPUT', 'auth set --stdin received an empty API key');
88
85
  }
89
- fail('INVALID_INPUT', `auth set requires <licenseKey> or ${LICENSE_KEY_ENV_VAR}`);
86
+ const envApiKey = resolveEnvApiKey();
87
+ if (envApiKey) {
88
+ return envApiKey;
89
+ }
90
+ if (process.stdin.isTTY && process.stderr.isTTY) {
91
+ const promptValue = await readSecretFromPrompt('Merchbase API key: ');
92
+ if (promptValue) {
93
+ return promptValue;
94
+ }
95
+ fail('INVALID_INPUT', 'auth set received an empty API key');
96
+ }
97
+ fail('INVALID_INPUT', `auth set requires <apiKey>, --stdin, or ${API_KEY_ENV_VAR}`);
98
+ };
99
+ const readSecretFromStdin = async () => {
100
+ const chunks = [];
101
+ for await (const chunk of process.stdin) {
102
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
103
+ }
104
+ return Buffer.concat(chunks).toString('utf8').trim();
105
+ };
106
+ const readSecretFromPrompt = async (prompt) => {
107
+ process.stderr.write(prompt);
108
+ setTerminalEcho(false);
109
+ const rl = readline.createInterface({
110
+ input: process.stdin,
111
+ output: process.stderr,
112
+ terminal: true,
113
+ });
114
+ try {
115
+ const value = await new Promise(resolve => {
116
+ rl.question('', resolve);
117
+ });
118
+ process.stderr.write('\n');
119
+ return value.trim();
120
+ }
121
+ finally {
122
+ rl.close();
123
+ setTerminalEcho(true);
124
+ }
125
+ };
126
+ const setTerminalEcho = (enabled) => {
127
+ if (process.platform === 'win32' || !process.stdin.isTTY) {
128
+ return;
129
+ }
130
+ spawnSync('stty', [enabled ? 'echo' : '-echo'], {
131
+ stdio: ['inherit', 'ignore', 'ignore'],
132
+ });
90
133
  };
91
- const resolveEnvLicenseKey = () => {
92
- const value = process.env[LICENSE_KEY_ENV_VAR]?.trim();
134
+ const resolveEnvApiKey = () => {
135
+ const value = process.env[API_KEY_ENV_VAR]?.trim();
93
136
  return value ? value : undefined;
94
137
  };
95
138
  const createSecureStore = async () => {
@@ -127,7 +170,7 @@ const getSecureStoreBackendLabel = () => {
127
170
  }
128
171
  };
129
172
  const createFileSecretStore = (secretStoreDir) => {
130
- const secretPath = path.join(secretStoreDir, 'license-key.json');
173
+ const secretPath = path.join(secretStoreDir, 'api-key.json');
131
174
  return {
132
175
  backend: 'test-file-store',
133
176
  serviceName: AUTH_SERVICE_NAME,
@@ -136,7 +179,7 @@ const createFileSecretStore = (secretStoreDir) => {
136
179
  try {
137
180
  const raw = await readFile(secretPath, 'utf8');
138
181
  const parsed = JSON.parse(raw);
139
- return typeof parsed.licenseKey === 'string' ? parsed.licenseKey : null;
182
+ return typeof parsed.apiKey === 'string' ? parsed.apiKey : null;
140
183
  }
141
184
  catch (error) {
142
185
  if (error &&
@@ -150,7 +193,7 @@ const createFileSecretStore = (secretStoreDir) => {
150
193
  },
151
194
  set: async (value) => {
152
195
  await mkdir(secretStoreDir, { recursive: true });
153
- await writeFile(secretPath, `${JSON.stringify({ licenseKey: value }, null, 2)}\n`, 'utf8');
196
+ await writeFile(secretPath, `${JSON.stringify({ apiKey: value }, null, 2)}\n`, 'utf8');
154
197
  },
155
198
  clear: async () => {
156
199
  try {
@@ -9,11 +9,93 @@ export declare const runConfigCommand: (command: ConfigCommand, config: CliConfi
9
9
  path: string;
10
10
  globalPath: string;
11
11
  config: CliConfig;
12
+ auth: {
13
+ source: string;
14
+ envOverride: boolean;
15
+ secureStore: {
16
+ backend: string;
17
+ serviceName: string;
18
+ accountName: string;
19
+ available: boolean;
20
+ hasStoredApiKey: boolean;
21
+ error?: undefined;
22
+ };
23
+ } | {
24
+ source: string;
25
+ envOverride: boolean;
26
+ secureStore: {
27
+ backend: string;
28
+ serviceName: string;
29
+ accountName: string;
30
+ available: boolean;
31
+ hasStoredApiKey: null;
32
+ error: string;
33
+ };
34
+ };
12
35
  } | {
13
- cleared: boolean;
36
+ key: "base-url" | "marketplace" | "storage-dir";
37
+ value: string | null;
38
+ } | {
39
+ unset: "base-url" | "marketplace" | "storage-dir";
40
+ storageDir: string;
41
+ path: string;
42
+ globalPath: string;
43
+ config: CliConfig;
44
+ auth: {
45
+ source: string;
46
+ envOverride: boolean;
47
+ secureStore: {
48
+ backend: string;
49
+ serviceName: string;
50
+ accountName: string;
51
+ available: boolean;
52
+ hasStoredApiKey: boolean;
53
+ error?: undefined;
54
+ };
55
+ } | {
56
+ source: string;
57
+ envOverride: boolean;
58
+ secureStore: {
59
+ backend: string;
60
+ serviceName: string;
61
+ accountName: string;
62
+ available: boolean;
63
+ hasStoredApiKey: null;
64
+ error: string;
65
+ };
66
+ };
67
+ key?: undefined;
68
+ value?: undefined;
69
+ } | {
70
+ reset: boolean;
14
71
  storageDir: string;
15
72
  path: string;
16
73
  globalPath: string;
17
74
  config: CliConfig;
75
+ auth: {
76
+ source: string;
77
+ envOverride: boolean;
78
+ secureStore: {
79
+ backend: string;
80
+ serviceName: string;
81
+ accountName: string;
82
+ available: boolean;
83
+ hasStoredApiKey: boolean;
84
+ error?: undefined;
85
+ };
86
+ } | {
87
+ source: string;
88
+ envOverride: boolean;
89
+ secureStore: {
90
+ backend: string;
91
+ serviceName: string;
92
+ accountName: string;
93
+ available: boolean;
94
+ hasStoredApiKey: null;
95
+ error: string;
96
+ };
97
+ };
98
+ key?: undefined;
99
+ value?: undefined;
18
100
  }>;
19
101
  export {};
@@ -1,14 +1,31 @@
1
- import { clearConfig, saveConfig, switchStorageDir } from './cli-config.js';
1
+ import { buildAuthStatus } from './cli-auth.js';
2
+ import { resetConfig, saveConfig, switchStorageDir, unsetStorageDir, } from './cli-config.js';
2
3
  import { normalizeBaseUrl } from './cli-options.js';
4
+ const CONFIG_KEYS = ['base-url', 'marketplace', 'storage-dir'];
3
5
  export const runConfigCommand = async (command, config, paths, fail) => {
4
6
  if (command.verb === 'show') {
5
7
  return buildConfigResponse(paths, config);
6
8
  }
7
- if (command.verb === 'clear') {
8
- await clearConfig(paths);
9
+ if (command.verb === 'get') {
10
+ const key = requireConfigKey(command.args[0], 'config get', fail);
9
11
  return {
10
- ...buildConfigResponse(paths, {}),
11
- cleared: true,
12
+ key,
13
+ value: getConfigValue(key, paths, config),
14
+ };
15
+ }
16
+ if (command.verb === 'unset') {
17
+ const key = requireConfigKey(command.args[0], 'config unset', fail);
18
+ const nextState = await unsetConfigValue(key, paths, config);
19
+ return {
20
+ ...(await buildConfigResponse(nextState.paths, nextState.config)),
21
+ unset: key,
22
+ };
23
+ }
24
+ if (command.verb === 'reset') {
25
+ const nextState = await resetConfig(paths);
26
+ return {
27
+ ...(await buildConfigResponse(nextState.paths, nextState.config)),
28
+ reset: true,
12
29
  };
13
30
  }
14
31
  if (command.verb !== 'set') {
@@ -38,19 +55,64 @@ export const runConfigCommand = async (command, config, paths, fail) => {
38
55
  nextConfig.marketplaceId = value;
39
56
  }
40
57
  else {
41
- fail('INVALID_INPUT', 'unsupported config key', {
42
- key,
43
- supportedKeys: ['base-url', 'marketplace', 'storage-dir'],
44
- });
58
+ failUnsupportedConfigKey(key, fail);
45
59
  }
46
60
  await saveConfig(paths, nextConfig);
47
61
  return buildConfigResponse(paths, nextConfig);
48
62
  };
49
- const buildConfigResponse = (paths, config) => {
63
+ const buildConfigResponse = async (paths, config) => {
50
64
  return {
51
65
  storageDir: paths.storageDir,
52
66
  path: paths.configPath,
53
67
  globalPath: paths.globalConfigPath,
54
68
  config,
69
+ auth: await buildAuthStatus(),
70
+ };
71
+ };
72
+ const requireConfigKey = (key, commandName, fail) => {
73
+ if (!key) {
74
+ fail('INVALID_INPUT', `${commandName} requires <key>`, {
75
+ supportedKeys: CONFIG_KEYS,
76
+ });
77
+ }
78
+ if (isConfigKey(key)) {
79
+ return key;
80
+ }
81
+ return failUnsupportedConfigKey(key, fail);
82
+ };
83
+ const isConfigKey = (key) => {
84
+ return CONFIG_KEYS.includes(key);
85
+ };
86
+ const getConfigValue = (key, paths, config) => {
87
+ switch (key) {
88
+ case 'base-url':
89
+ return config.baseUrl ?? null;
90
+ case 'marketplace':
91
+ return config.marketplaceId ?? null;
92
+ case 'storage-dir':
93
+ return paths.storageDir;
94
+ }
95
+ };
96
+ const unsetConfigValue = async (key, paths, config) => {
97
+ if (key === 'storage-dir') {
98
+ return unsetStorageDir(paths);
99
+ }
100
+ const nextConfig = { ...config };
101
+ if (key === 'base-url') {
102
+ nextConfig.baseUrl = undefined;
103
+ }
104
+ else {
105
+ nextConfig.marketplaceId = undefined;
106
+ }
107
+ await saveConfig(paths, nextConfig);
108
+ return {
109
+ paths,
110
+ config: nextConfig,
55
111
  };
56
112
  };
113
+ const failUnsupportedConfigKey = (key, fail) => {
114
+ fail('INVALID_INPUT', 'unsupported config key', {
115
+ key,
116
+ supportedKeys: CONFIG_KEYS,
117
+ });
118
+ };
@@ -19,8 +19,15 @@ export declare const loadCliContext: () => Promise<{
19
19
  config: CliConfig;
20
20
  }>;
21
21
  export declare const loadCliPathsOrDefault: () => Promise<CliPaths>;
22
- export declare const clearConfig: (paths: CliPaths) => Promise<void>;
22
+ export declare const resetConfig: (paths: CliPaths) => Promise<{
23
+ paths: CliPaths;
24
+ config: {};
25
+ }>;
23
26
  export declare const saveConfig: (paths: CliPaths, config: CliConfig) => Promise<void>;
27
+ export declare const unsetStorageDir: (paths: CliPaths) => Promise<{
28
+ paths: CliPaths;
29
+ config: CliConfig;
30
+ }>;
24
31
  export declare const switchStorageDir: ({ currentConfig, currentPaths, requestedStorageDir, }: {
25
32
  currentConfig: CliConfig;
26
33
  currentPaths: CliPaths;